Run geoserver within docker.
Based on the official tomcat docker image, specifically:
- Tomcat 9
- JDK 11 (eclipse temurin)
- Ubuntu Jammy (22.04 LTS)
- Ability to use a datadir from host machine for persistence.
- Set ADMIN_PASSWORD with env variable.
- Flexibility to mount other paths for persistence.
- Build geoserver webapp (WAR) file from local/remote URL.
- Run with custom geoserver WAR file during runtime.
- Helper script to reload geoserver during runtime.
- Helper script to test plugins during runtime.
- CORS support.
- Specify custom fonts during build time.
Most of the paths if mounted from the host to locations on the container such as $GEOSERVER_LOG_DIR
$CATALINA_BASE
$GEOWEBCACHE_CACHE_DIR
$GEOWEBCACHE_CONFIG_DIR
$NETCDF_DATA_DIR
$GRIB_CACHE_DIR
$GEOSERVER_DATA_DIR
should be owned by the UID of user that geoserver is running on, which at the moment is UID 1000. The paths should be recursively chown-ed with this UID. More info here
docker run --name gs -p 8080:8080 geosolutionsit/geoserver
Or for data persistence starting with default geoserver datadir (in this example GEOSERVER_DATA_DIR is pointing to /var/geoserver/datadir
):
docker run --rm --name gs -p 8080:8080 geosolutionsit/geoserver
Save datadir locally to have a starting datadir:
docker cp gs:/var/geoserver/datadir ./datadir
docker stop gs
start GeoServer with data persistence on saved datadir:
docker run -v ./datadir:/var/geoserver/datadir --name gs -p 8080:8080 geosolutionsit/geoserver
start GeoServer with data persistence on saved datadir and change admin password:
docker run -e ADMIN_PASSWORD=securepassword -v datadir:/var/geoserver/datadir --name gs -p 8080:8080 geosolutionsit/geoserver
Open your browser and point it to http://localhost:8080/geoserver
.
GeoServer web interface will show up, you can now log in with user admin and password geoserver
.
There are some environment variables you can use at runtime.
GEOSERVER_LOG_DIR
to customize log placementGEOSERVER_DATA_DIR
to put your GeoServer datadir elsewhereGEOWEBCACHE_CONFIG_DIR
to put your GeoServer cache configuration elsewhereGEOWEBCACHE_CACHE_DIR
to put your GeoServer cache elsewhereNETCDF_DATA_DIR
to put your GeoServer NETCDF data dir elsewhereGRIB_CACHE_DIR
to put your GeoServer GRIB cache dir elsewhere
Each of these variables can be associated to an external volume to persist data for example in a docker compose.
CATALINA_OPTS
to customizeCATALINA_OPTS
for the containerEXTRA_GEOSERVER_OPTS
to append toCATALINA_OPTS
JAIEXT_ENABLED
by default istrue
. More info here.
INITIAL_MEMORY
by default is2G
. (-Xms
)MAXIMUM_MEMORY
by default is4G
(-Xmx
)
CORS headers can be configured with env variables (they are also build arguments):
CORS_ENABLED
to true to enable CORS support. The following environment variables can be used to customize the CORS configuration.CORS_ALLOWED_ORIGINS
(default*
)CORS_ALLOWED_METHODS
(defaultGET,POST,PUT,DELETE,HEAD,OPTIONS
)CORS_ALLOWED_HEADERS
(default*
)CORS_ALLOW_CREDENTIALS
(defaultfalse
) Setting this to true will only have the desired effect ifCORS_ALLOWED_ORIGINS
defines explicit origins (not *)
Example of how to build a docker image with just geoserver war and then add plugins at runtime.
docker build -t geoserver:test-2.19.1 \
--build-arg GIT_HASH=`git show -s --format=%H` \
--build-arg GEOSERVER_WEBAPP_SRC=https://sourceforge.net/projects/geoserver/files/GeoServer/2.19.1/geoserver-2.19.1-war.zip/download .
docker run \
--env PLUGIN_DYNAMIC_URLS="http://sourceforge.net/projects/geoserver/files/GeoServer/2.19.1/extensions/geoserver-2.19.1-control-flow-plugin.zip \
http://sourceforge.net/projects/geoserver/files/GeoServer/2.19.1/extensions/geoserver-2.19.1-libjpeg-turbo-plugin.zip" \
--rm --name gs -p 8080:8080 geoserver:test-2.19.1
Docker Compose is a tool that helps us easily handle multiple containers at once.
Install instructions: Docker Docs (Not required if you have Docker Desktop, where it's pre-installed)
In order to use compose we need first to set correctly the "docker-compose.yml" file of the docker-geoserver.
In order to persist and externalize access to the data of the geoserver container we need to set the values of the environment variables (named in the previous section) on the container and then associated this to the external volumes we going to create.
To achieve this, first we gonna create a .env file (in the same folder of the docker-compose.yml file) to define in an optimal way (easy to modify later) the environment variables values for the geoserver container:
.env file content:
GEOSERVER_LOG_DIR=/var/geoserver/logs
GEOSERVER_DATA_DIR=/var/geoserver/datadir
GEOWEBCACHE_CONFIG_DIR=/var/geoserver/datadir/gwc
GEOWEBCACHE_CACHE_DIR=/var/geoserver/gwc_cache_dir
NETCDF_DATA_DIR=/var/geoserver/netcdf_data_dir
GRIB_CACHE_DIR=/var/geoserver/grib_cache_dir
More details on the definition of the .env file: Docker - The Compose Specification
Then we are going to modify the docker-compose configuration file to set environment variables in the geoserver container with the “environment” key:
...
geoserver:
build:
context: .
dockerfile: ./Dockerfile
...
environment:
- GEOSERVER_LOG_DIR=${GEOSERVER_LOG_DIR}
- GEOSERVER_DATA_DIR=${GEOSERVER_DATA_DIR}
- GEOWEBCACHE_CONFIG_DIR=${GEOWEBCACHE_CONFIG_DIR}
- GEOWEBCACHE_CACHE_DIR=${GEOWEBCACHE_CACHE_DIR}
- NETCDF_DATA_DIR=${NETCDF_DATA_DIR}
- GRIB_CACHE_DIR=${GRIB_CACHE_DIR}
...
To be sure that the environment variables are not pass empty, you can set a default value.
Example:
...
geoserver:
...
environment:
- GEOSERVER_LOG_DIR=${GEOSERVER_LOG_DIR:-/var/geoserver/logs}
...
If GEOSERVER_LOG_DIR variable is not set in the .env file, is going to take his default value.
Next we are going to define the external volumes, modifying again the docker-compose configuration file.
services:
...
geoserver:
...
volumes:
- logs:${GEOSERVER_LOG_DIR}
- datadir:${GEOSERVER_DATA_DIR}
- gwc_config:${GEOWEBCACHE_CONFIG_DIR}
- gwc:${GEOWEBCACHE_CACHE_DIR}
- netcfd:${NETCDF_DATA_DIR}
- grib_cache:${GRIB_CACHE_DIR}
...
volumes:
pg_data:
logs:
datadir:
gwc_config:
gwc:
netcfd:
grib_cache:
Both configurations together (environment variables and external volumes) are going to show like this in the docker-compose configuration file:
services:
...
geoserver:
build:
context: .
dockerfile: ./Dockerfile
...
environment:
- GEOSERVER_LOG_DIR=${GEOSERVER_LOG_DIR}
- GEOSERVER_DATA_DIR=${GEOSERVER_DATA_DIR}
- GEOWEBCACHE_CONFIG_DIR=${GEOWEBCACHE_CONFIG_DIR}
- GEOWEBCACHE_CACHE_DIR=${GEOWEBCACHE_CACHE_DIR}
- NETCDF_DATA_DIR=${NETCDF_DATA_DIR}
- GRIB_CACHE_DIR=${GRIB_CACHE_DIR}
volumes:
- logs:${GEOSERVER_LOG_DIR}
- datadir:${GEOSERVER_DATA_DIR}
- gwc_config:${GEOWEBCACHE_CONFIG_DIR}
- gwc:${GEOWEBCACHE_CACHE_DIR}
- netcfd:${NETCDF_DATA_DIR}
- grib_cache:${GRIB_CACHE_DIR}
...
volumes:
pg_data:
logs:
datadir:
gwc_config:
gwc:
netcfd:
grib_cache:
After this our geoserver container is ready and persisting his data.
For more details about volumes, check the documentation: Docker - Volume
In the docker-compose.yml file, actually we are building the GeoServer container from a image on a URL.
...
geoserver:
build:
context: .
dockerfile: ./Dockerfile
args:
GEOSERVER_WEBAPP_SRC: "https://build.geoserver.org/geoserver/main/geoserver-main-latest-war.zip"
container_name: geoserver
...
This is dynamic, you can use a local file in the host to build the container as and alternative if you need. In order to do this, we need to modify the docker-compose configuration file like this:
...
geoserver:
build:
context: .
dockerfile: ./Dockerfile
args:
GEOSERVER_WEBAPP_SRC: "/host/directory/alternativegeoserver.war"
container_name: geoserver
...
This option allows you to use URLs and local files as well to build the GeoServer container in the option that suit you best.
For more details, check the ADD documentation: Docker - ADD
- Example configuration for the geoserver service:
...
geoserver:
image: geosolutionsit/geoserver:2.23.0 ## Initially, include an image to avoid Docker complaints.
volumes:
- /path/custom-war:/usr/local/tomcat/webapps/geoserver ## Define a volume pointing to your custom .war, ensuring it's unzipped.
environment:
- EXTRA_GEOSERVER_OPTS="-DGEOSERVER_CSRF_WHITELIST=example.org -DENABLE_JSONP=true"
container_name: geoserver
depends_on:
postgres:
condition: service_healthy
ports:
- 8080
networks:
- geoserver-network
...
-Identify User ID: Determine the user ID running Geoserver inside the container. Use docker exec to access the container and run the id command.
-Adjust Permissions: On the host system, use chown to set the owner of the directory containing Geoserver files to match the user ID. Then, use chmod to set appropriate permissions.
-Assuming user ID is 1000 and directory is /path/custom-war
sudo chown -R 1000:1000 /path/custom-war
sudo chmod -R 755 /path/custom-war
Set the CUSTOM_FONTS
build argument to a path on the host with the fonts. This will be copied to the image during docker build.
Containers communicate between themselves in networks created, implicitly or through configuration, by docker compose. To reach a container from the host, the ports must be exposed declaratively through the "ports" keyword, which also allows us to choose if we want exposing the port differently in the host.
ports:
- "bind_address:hostport:containerport" # bind_address:hostport:containerport SHOULD always be specified as a (quoted) string, to avoid conflicts with yaml base-60 float.
The Host port and the Container Port can be equal or no, this option allows us to run different containers exposing the same ports without collisions.
It is advised to specify the bind_address
on the host explicitly to avoid security issues.
GeoServer docker-compose.yml:
services:
postgres:
image: postgis/postgis
container_name: postgres
...
ports:
- 5432
...
geoserver:
...
container_name: geoserver
...
ports:
- 8080
...
proxy:
image: nginx
container_name: proxy
...
ports:
- "80:80"
...
In this example the only port visible in the host will be port 80 of the proxy container.
In order to access the postgresql server from outside the container, we need to use the "port" option to expose a port.
services:
postgres:
image: postgis/postgis
container_name: postgres
...
ports:
- "5432:5432"
...
To test the expose, we can use "curl" command in the host:
curl -v localhost:5432
* Trying 127.0.0.1:5432...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5432 (#0)
> GET / HTTP/1.1
> Host: localhost:5432
> User-Agent: curl/7.68.0
> Accept: */*
More details on expose containers ports: Docker - The Compose Specification
When we have everything configured with the docker-compose.yml file, to start the containers for the first time we gonna use this command (located in the directory when the yml file is):
docker compose up
This is gonna create and start the containers, the networks, and the volumes defined in the docker-compose.yml file. This is the command you need to use every time after a change on the docker-compose.yml file in order to apply the modifications.
After the first time, we can simply use this command to start the containers:
docker compose start
Console output:
Starting postgres ... done
Starting geoserver ... done
Starting proxy ... done
...
(Continued with the proxy logs)
To stopping all the containers, this is the command:
docker compose stop
Console output:
Stopping proxy ... done
Stopping geoserver ... done
Stopping postgres ... done
If you want to reset the status of the containers, we need to run this command, which will destroy everything with only the exception of external volumes:
docker compose down
Make sure you have your war file at ./geoserver.war
docker build --build-arg GEOSERVER_WEBAPP_SRC="./geoserver.war" -t geoserver:test .
There are build arguments to customize the image:
PLUG_IN_URLS
space-separated list of additional plugins for geoserver (see examples), this works both for extensions and community plugins.GEOSERVER_DATA_DIR_SRC
add a customized datadir to the final image. This can be a local zip or directory or remote URL (see ADD documentation)GEOSERVER_WEBAPP_SRC
to add your own custom web app to the final image. This can be a local zip or directory or remote URL (see ADD instruction Doc). If you want to build or package your own web app you can customize the "mother" stage of Dockerfile accordingly, if you want to download directly GeoServer you may need to add/download
at the end of download url which you can copy/paste from GeoServer official downloads page, see last example below
# Example of how to build a single customized war of geoserver or simply any vanilla one
docker build -t geoserver:test . --build-arg GEOSERVER_WEBAPP_SRC="./resources/geoserver/geoserver.war"
# Same kind of build as above but burning custom datadir inside GeoServer Docker image
docker build -t geoserver:test . --build-arg GEOSERVER_WEBAPP_SRC="./resources/geoserver/geoserver.war" --build-arg GEOSERVER_DATA_DIR_SRC="./resources/geoserver-datadir/"
# Example on how to download and build a geoserver version with stable plugins controlflow and libjpegturbo plugins burned in the image
docker build -t geoserver:luca-test-2.19.1 --build-arg GEOSERVER_WEBAPP_SRC="https://sourceforge.net/projects/geoserver/files/GeoServer/2.19.1/geoserver-2.19.1-war.zip/download" --build-arg PLUG_IN_URLS="http://sourceforge.net/projects/geoserver/files/GeoServer/2.19.1/extensions/geoserver-2.19.1-control-flow-plugin.zip http://sourceforge.net/projects/geoserver/files/GeoServer/2.19.1/extensions/geoserver-2.19.1-libjpeg-turbo-plugin.zip" .
While the container is running you can reload geoserver with:
docker exec -it <your-container-name> bash /usr/local/bin/geoserver-rest-reload.sh
docker exec -it <your-container-name> bash -c 'geoserver-plugin-download.sh $CATALINA_BASE/webapps/$APP_LOCATION/WEB-INF/lib <space separated list of plugin urls>'
Scripts provided that are for docker hub are under hooks
directory.
Basically the hooks/build
script takes these environment variables with current version numbers offered for geoserver:
export MAINT_VERSION="2.17.3 2.17.2 2.17.1"
export MIDDLE_STABLE="18"
export NIGHTLY_MAINT_VERSION="2.17.x"
export NIGHTLY_MASTER_VERSION="master foobar"
export NIGHTLY_STABLE_VERSION="2.18.x"
export STABLE_VERSION="2.18.1 2.18.0"
Notes:
Phantom version foobar
is supposed to always fail as a test and always tried to be built.
"MIDDLE_STABLE" has just a function for the scripts logic, increase it with latest minor version number for stable.
To test locally build hook you can use the test_hooks.sh
script provided.
the script can be run with no parameters to show the needed parameters:
./custom_build.sh
Usage: ./custom_build.sh [docker image tag] [geoserver version] [geoserver master version] [datadir| nodatadir] [pull|no pull];
[docker image tag] : the tag to be used for the docker iamge
[geoserver version] : the release version of geoserver to be used; you can set it to master if you want the last release
[geoserver master version] : if you use the master version for geoserver you need to set it to the numerical value for the next release;
if you use a released version you need to put it to the release number
[datadir| nodatadir]: if this parameter is equal to nodatadir the datadir is not burned in the docker images
[pull|no pull]: docker build use always a remote image or a local image
This script is meant to be used by automated build, variety of tests with highly customized versions of geoserver.
./custom_build.sh my-docker-tag 2.18.x 2.18.x nodatadir no_pull
This argument provides git hash information from inside of container. In order to get git hash information inside of container add this argument to the build line. As requirement git command should be installed.
--build-arg GIT_HASH=git show -s --format=%H
Below command shows git hash information.
docker exec -it bash -c 'echo $GIT_HASH'