diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..92bc551
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.iml
+*.idea
+
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..3afd9e3
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,87 @@
+FROM ubuntu:22.04
+
+# The GS_VERSION argument could be used like this to overwrite the default:
+# docker build --build-arg GS_VERSION=2.11.3 -t geoserver:2.11.3 .
+ARG TOMCAT_VERSION=9.0.64
+ARG GS_VERSION=2.21.0
+ARG GS_DATA_PATH=./geoserver_data/
+ARG ADDITIONAL_LIBS_PATH=./additional_libs/
+ARG ADDITIONAL_FONTS_PATH=./additional_fonts/
+ARG CORS_ENABLED=false
+ARG CORS_ALLOWED_ORIGINS=*
+ARG CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,HEAD,OPTIONS
+ARG CORS_ALLOWED_HEADERS=*
+ARG STABLE_PLUGIN_URL=https://sourceforge.net/projects/geoserver/files/GeoServer/${GS_VERSION}/extensions
+
+# Environment variables
+ENV CATALINA_HOME=/opt/apache-tomcat-${TOMCAT_VERSION}
+ENV GEOSERVER_VERSION=$GS_VERSION
+ENV GEOSERVER_DATA_DIR=/opt/geoserver_data/
+ENV GEOSERVER_LIB_DIR=$CATALINA_HOME/webapps/geoserver/WEB-INF/lib/
+ENV EXTRA_JAVA_OPTS="-Xms256m -Xmx1g"
+ENV CORS_ENABLED=$CORS_ENABLED
+ENV CORS_ALLOWED_ORIGINS=$CORS_ALLOWED_ORIGINS
+ENV CORS_ALLOWED_METHODS=$CORS_ALLOWED_METHODS
+ENV CORS_ALLOWED_HEADERS=$CORS_ALLOWED_HEADERS
+ENV DEBIAN_FRONTEND=noninteractive
+ENV INSTALL_EXTENSIONS=false
+ENV STABLE_EXTENSIONS=''
+ENV STABLE_PLUGIN_URL=$STABLE_PLUGIN_URL
+ENV ADDITIONAL_LIBS_DIR=/opt/additional_libs/
+ENV ADDITIONAL_FONTS_DIR=/opt/additional_fonts/
+
+# see http://docs.geoserver.org/stable/en/user/production/container.html
+ENV CATALINA_OPTS="\$EXTRA_JAVA_OPTS \
+ -Djava.awt.headless=true -server \
+ -Dfile.encoding=UTF-8 \
+ -Djavax.servlet.request.encoding=UTF-8 \
+ -Djavax.servlet.response.encoding=UTF-8 \
+ -D-XX:SoftRefLRUPolicyMSPerMB=36000 \
+ -Xbootclasspath/a:$CATALINA_HOME/lib/marlin.jar \
+ -Xbootclasspath/a:$CATALINA_HOME/lib/marlin-sun-java2d.jar \
+ -Dsun.java2d.renderer=org.marlin.pisces.PiscesRenderingEngine \
+ -Dorg.geotools.coverage.jaiext.enabled=true"
+
+# init
+RUN apt update && \
+ apt -y upgrade && \
+ apt install -y --no-install-recommends openssl unzip gdal-bin wget curl openjdk-11-jdk && \
+ rm -rf $CATALINA_HOME/webapps/* && \
+ apt clean && \
+ rm -rf /var/cache/apt/* && \
+ rm -rf /var/lib/apt/lists/*
+
+WORKDIR /opt/
+RUN wget -q https://dlcdn.apache.org/tomcat/tomcat-9/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz && \
+ tar xf apache-tomcat-${TOMCAT_VERSION}.tar.gz && \
+ rm apache-tomcat-${TOMCAT_VERSION}.tar.gz && \
+ rm -rf /opt/apache-tomcat-${TOMCAT_VERSION}/webapps/ROOT && \
+ rm -rf /opt/apache-tomcat-${TOMCAT_VERSION}/webapps/docs && \
+ rm -rf /opt/apache-tomcat-${TOMCAT_VERSION}/webapps/examples
+
+WORKDIR /tmp
+
+# install geoserver
+RUN wget -q -O /tmp/geoserver.zip http://downloads.sourceforge.net/project/geoserver/GeoServer/$GEOSERVER_VERSION/geoserver-$GEOSERVER_VERSION-war.zip && \
+ unzip geoserver.zip geoserver.war -d $CATALINA_HOME/webapps && \
+ mkdir -p $CATALINA_HOME/webapps/geoserver && \
+ unzip -q $CATALINA_HOME/webapps/geoserver.war -d $CATALINA_HOME/webapps/geoserver && \
+ rm $CATALINA_HOME/webapps/geoserver.war && \
+ mkdir -p $GEOSERVER_DATA_DIR
+
+COPY $GS_DATA_PATH $GEOSERVER_DATA_DIR
+COPY $ADDITIONAL_LIBS_PATH $GEOSERVER_LIB_DIR
+COPY $ADDITIONAL_FONTS_PATH /usr/share/fonts/truetype/
+
+# cleanup
+RUN apt purge -y && \
+ apt autoremove --purge -y && \
+ rm -rf /tmp/*
+
+# copy scripts
+COPY *.sh /opt/
+RUN chmod +x /opt/*.sh
+
+ENTRYPOINT /opt/startup.sh
+
+WORKDIR /opt
diff --git a/README.md b/README.md
index 0d2e45c..93d48b6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,80 @@
-# docker
-GeoServer docker image
+# A geoserver docker image
+
+This Dockerfile can be used to create images for all geoserver versions since 2.5.
+
+* Debian based Linux
+* OpenJDK 11
+* Tomcat 9
+* GeoServer
+ * Support of custom fonts (e.g. for SLD styling)
+ * CORS support
+
+**IMPORTANT NOTE:** Please change the default geoserver master password! The default masterpw is located in this file (within the docker container): `/opt/geoserver_data/security/masterpw/default/masterpw`
+
+## How to build?
+
+`docker build -t {YOUR_TAG} .`
+
+## How to quickstart?
+
+Build the image as described above, then:
+
+`docker run -it -p 80:8080 {YOUR_TAG}`
+
+or if you want to start the container daemonized:
+
+`docker run -d -p 80:8080 {YOUR_TAG}`
+
+Check http://localhost/geoserver to see the geoserver page and login with geoserver defaults `admin:geoserver`
+
+## How to build a specific GeoServer version?
+
+`docker build --build-arg GS_VERSION={YOUR_VERSION} -t {YOUR_TAG} .`
+
+## How to build with custom geoserver data?
+
+`docker build --build-arg GS_DATA_PATH={RELATIVE_PATH_TO_YOUR_GS_DATA} .`
+
+**Note:** The passed path **must not** be absolute! Instead, the path should be within the build context (e.g. next to the Dockerfile) and should be passed as a relative path, e.g. `GS_DATA_PATH=./my_data/`
+
+## Can I build a specific GS version with custom data?
+
+Yes! Just pass the `--build-arg` param twice, e.g.
+
+`... --build-arg GS_VERSION={VERSION} --build-arg GS_DATA_PATH={PATH} ...`
+
+## How to build with additional libs/extensions/plugins?
+
+Put your `*.jar` files (e.g. the WPS extension) in the `additional_libs` folder and build with one of the commands from above! (They will be copied to the GeoServer `WEB-INF/lib` folder during the build.)
+
+**Note:** Similar to the GeoServer data path from above, you can also configure the path to the additional libraries by passing the `ADDITIONAL_LIBS_PATH` argument when building:
+
+`--build-arg ADDITIONAL_LIBS_PATH={RELATIVE_PATH_TO_YOUR_LIBS}`
+
+## How to add additional libs using an existing docker image?
+
+If you want to add geoserver extensions/libs by using a mount, you can add something like
+
+```
+--mount src="/dir/with/libs/on/host",target=/opt/additional_libs,type=bind
+```
+
+## How to add additional fonts to the docker image (e.g. for SLD styling)?
+
+If you want to add custom fonts (the base image only contains 26 fonts) by using a mount, you can add something like
+
+```
+--mount src="/dir/with/fonts/on/host",target=/opt/additional_fonts,type=bind
+```
+
+to your `docker run` command.
+
+**Note:** Do not change the target value!
+
+## How to watch geoserver.log from host?
+
+`docker exec -it {CONTAINER_ID} tail -f /opt/geoserver_data/logs/geoserver.log`
+
+## How to use the docker-compose demo?
+
+`docker-compose -f docker-compose-demo.yml up --build`
diff --git a/additional_fonts/.gitignore b/additional_fonts/.gitignore
new file mode 100644
index 0000000..76bedae
--- /dev/null
+++ b/additional_fonts/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+
diff --git a/additional_libs/.gitignore b/additional_libs/.gitignore
new file mode 100644
index 0000000..76bedae
--- /dev/null
+++ b/additional_libs/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+
diff --git a/docker-compose-demo.yml b/docker-compose-demo.yml
new file mode 100644
index 0000000..8ea844e
--- /dev/null
+++ b/docker-compose-demo.yml
@@ -0,0 +1,21 @@
+version: '3'
+services:
+ geoserver:
+ build:
+ context: .
+ args:
+ - GEOSERVER_VERSION=2.21.0
+ - CORS_ENABLED=true
+ - CORS_ALLOWED_METHODS=GET,POST,PUT,HEAD,OPTIONS
+ ports:
+ - 8080:8080
+ environment:
+ - INSTALL_EXTENSIONS=true
+ - STABLE_EXTENSIONS=wps,csw
+ - INITIAL_MEMORY=1G
+ - MAXIMUM_MEMORY=2G
+ - JAIEXT_ENABLED=true
+ - GEOSERVER_DATA_DIR=/opt/geoserver_data/
+ volumes:
+ - ./demo_data/geoserver_data:/opt/geoserver_data/:Z
+ - ./demo_data/additional_libs:/opt/additional_libs:Z # by mounting this we can install libs from host on startup
diff --git a/geoserver_data/.gitignore b/geoserver_data/.gitignore
new file mode 100644
index 0000000..76bedae
--- /dev/null
+++ b/geoserver_data/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+
diff --git a/install-extensions.sh b/install-extensions.sh
new file mode 100644
index 0000000..541cbd3
--- /dev/null
+++ b/install-extensions.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+# Inspired by https://github.com/kartoza/docker-geoserver
+
+function download_extension() {
+ URL=$1
+ EXTENSION=$2
+ DOWNLOAD_FILE="${ADDITIONAL_LIBS_DIR}geoserver-${GEOSERVER_VERSION}-${EXTENSION}-plugin.zip"
+
+ if [ -e "$DOWNLOAD_FILE" ]; then
+ echo "$DOWNLOAD_FILE already exists. Skipping download."
+ else
+ if curl --output /dev/null --silent --head --fail "${URL}"; then
+ echo -e "\nDownloading ${EXTENSION}-extension from ${URL} to ${DOWNLOAD_FILE}"
+ wget --progress=bar:force:noscroll -c --no-check-certificate "${URL}" -O ${DOWNLOAD_FILE}
+ else
+ echo "URL does not exist: ${URL}"
+ fi
+ fi
+}
+
+# Download stable plugins only if INSTALL_EXTENSIONS is true
+if [ "$INSTALL_EXTENSIONS" = "true" ]; then
+ echo "Starting download of extensions"
+ for EXTENSION in $(echo "${STABLE_EXTENSIONS}" | tr ',' ' '); do
+ URL="${STABLE_PLUGIN_URL}/geoserver-${GEOSERVER_VERSION}-${EXTENSION}-plugin.zip"
+ download_extension ${URL} ${EXTENSION}
+ done
+ echo "Finished download of extensions"
+fi
+
+# Install the extensions
+echo "Starting installation of extensions"
+for EXTENSION in $(echo "${STABLE_EXTENSIONS}" | tr ',' ' '); do
+ ADDITIONAL_LIB=${ADDITIONAL_LIBS_DIR}geoserver-${GEOSERVER_VERSION}-${EXTENSION}-plugin.zip
+ [ -e "$ADDITIONAL_LIB" ] || continue
+
+ if [[ $ADDITIONAL_LIB == *.zip ]]; then
+ unzip -q -o -d ${GEOSERVER_LIB_DIR} ${ADDITIONAL_LIB} "*.jar"
+ echo "Installed all jar files from ${ADDITIONAL_LIB}"
+ elif [[ $ADDITIONAL_LIB == *.jar ]]; then
+ cp ${ADDITIONAL_LIB} ${GEOSERVER_LIB_DIR}
+ echo "Installed ${ADDITIONAL_LIB}"
+ else
+ echo "Skipping ${ADDITIONAL_LIB}: unknown file extension."
+ fi
+done
+echo "Finished installation of extensions"
diff --git a/startup.sh b/startup.sh
new file mode 100755
index 0000000..812e243
--- /dev/null
+++ b/startup.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+## install GeoServer extensions before starting the tomcat
+/opt/install-extensions.sh
+
+# copy additional geoserver libs before starting the tomcat
+# we also count whether at least one file with the extensions exists
+count=`ls -1 $ADDITIONAL_LIBS_DIR/*.jar 2>/dev/null | wc -l`
+if [ -d "$ADDITIONAL_LIBS_DIR" ] && [ $count != 0 ]; then
+ cp $ADDITIONAL_LIBS_DIR/*.jar $CATALINA_HOME/webapps/geoserver/WEB-INF/lib/
+fi
+
+# copy additional fonts before starting the tomcat
+# we also count whether at least one file with the extensions exists
+count=`ls -1 *.ttf 2>/dev/null | wc -l`
+if [ -d "$ADDITIONAL_FONTS_DIR" ] && [ $count != 0 ]; then
+ cp $ADDITIONAL_FONTS_DIR/*.ttf /usr/share/fonts/truetype/
+fi
+
+# configure CORS (inspired by https://github.com/oscarfonts/docker-geoserver)
+# if enabled, this will add the filter definitions
+# to the end of the web.xml
+# (this will only happen if our filter has not yet been added before)
+if [ "${CORS_ENABLED}" = "true" ]; then
+ if ! grep -q DockerGeoServerCorsFilter "$CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml"; then
+ sed -i "\::i\\
+ \n\
+ DockerGeoServerCorsFilter\n\
+ org.apache.catalina.filters.CorsFilter\n\
+ \n\
+ cors.allowed.origins\n\
+ ${CORS_ALLOWED_ORIGINS}\n\
+ \n\
+ \n\
+ cors.allowed.methods\n\
+ ${CORS_ALLOWED_METHODS}\n\
+ \n\
+ \n\
+ cors.allowed.headers\n\
+ ${CORS_ALLOWED_HEADERS}\n\
+ \n\
+ \n\
+ \n\
+ DockerGeoServerCorsFilter\n\
+ /*\n\
+ " "$CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml";
+ fi
+fi
+
+# start the tomcat
+$CATALINA_HOME/bin/catalina.sh run