Deploy GeoServer | (Just Updated) WMS WFS Map Server with PostGIS, Admin Password Preset, Data Survives Redeploys

GeoServer WMS/WFS/WMTS map server with a PostGIS spatial database

Deploy GeoServer | (Just Updated) WMS WFS Map Server with PostGIS, Admin Password Preset, Data Survives Redeploys

Just deployed

/var/lib/postgresql

Just deployed

/opt/geoserver/data_dir

Deploy and Host GeoServer on Railway

Built for an LLM agent to deploy and use. There is no setup form and nothing to fill in: the admin password is generated at deploy time, a PostGIS database is wired in on the private network, and the contract below lists every address, variable name and one script that proves the REST API, the database link and the WMS service work.

GeoServer is the open source Java server for publishing geospatial data as web services. It serves maps over OGC WMS, WMTS and WCS and vector features over WFS, reads PostGIS, GeoTIFF, shapefiles and GeoPackage, and has a web admin UI and a full REST API for configuration.

About Hosting GeoServer

This template runs two services. geoserver is the kartoza/geoserver 3.0.2 image (GeoServer 3.0.2 on Tomcat), pinned by digest, with its data directory on a volume at /opt/geoserver/data_dir. postgis is PostgreSQL 18 with PostGIS 3.6 on its own volume, reachable only on Railway's private network. Layers, styles, workspaces, stores and the admin password all live in the volume, so they survive redeploys.

The admin user is admin. Its password comes from GEOSERVER_ADMIN_PASSWORD, generated per deploy; the image applies it on every boot, so changing the variable and redeploying changes the password. Proxy headers are on, so the capabilities documents and the admin UI advertise the https:// address Railway serves. The JVM heap starts at 512 MB and tops out at 1 GB (INITIAL_MEMORY and MAXIMUM_MEMORY on the geoserver service raise it); the service measured about 750 MB resident when idle, so plan for at least 1 GB of memory.

Agent contract

  • Public URL: GEOSERVER_PUBLIC_URL (https:// plus the service's RAILWAY_PUBLIC_DOMAIN plus /geoserver). The admin UI is at /web/ under it.
  • Private URL: http://geoserver.railway.internal:8080/geoserver, exposed as GEOSERVER_PRIVATE_URL.
  • Credentials: user admin, password in GEOSERVER_ADMIN_PASSWORD. The database link uses POSTGIS_HOST and POSTGIS_PASSWORD (user postgres, database railway, port 5432, private network only).
  • Auth: HTTP basic on /rest/*; anonymous REST calls get 401. OGC services (/wms, /wfs, /ows) are open for published layers.
  • Health: GET /geoserver/wms returns 200 without credentials.
  • API: REST under /geoserver/rest/ (workspaces, datastores, layers, styles); OGC under /geoserver/WORKSPACE/wms, /wfs, /wcs.
# run with GEOSERVER_PUBLIC_URL, GEOSERVER_ADMIN_PASSWORD, POSTGIS_HOST and POSTGIS_PASSWORD set
AUTH="admin:$GEOSERVER_ADMIN_PASSWORD"
W="agent$(date +%s)"
curl -s -o /dev/null -w "health: %{http_code}\n" "$GEOSERVER_PUBLIC_URL/wms"
curl -s -o /dev/null -w "anonymous rest: %{http_code}\n" "$GEOSERVER_PUBLIC_URL/rest/workspaces.json"
curl -s -o /dev/null -w "workspace: %{http_code}\n" -u "$AUTH" -H "Content-Type: application/json" \
  -d "{\"workspace\":{\"name\":\"$W\"}}" "$GEOSERVER_PUBLIC_URL/rest/workspaces"
curl -s -o /dev/null -w "postgis store: %{http_code}\n" -u "$AUTH" -H "Content-Type: application/json" \
  -d "{\"dataStore\":{\"name\":\"pg\",\"connectionParameters\":{\"entry\":[{\"@key\":\"dbtype\",\"\$\":\"postgis\"},{\"@key\":\"host\",\"\$\":\"$POSTGIS_HOST\"},{\"@key\":\"port\",\"\$\":\"5432\"},{\"@key\":\"database\",\"\$\":\"railway\"},{\"@key\":\"schema\",\"\$\":\"public\"},{\"@key\":\"user\",\"\$\":\"postgres\"},{\"@key\":\"passwd\",\"\$\":\"$POSTGIS_PASSWORD\"}]}}}" \
  "$GEOSERVER_PUBLIC_URL/rest/workspaces/$W/datastores"
curl -s -o /dev/null -w "postgis reachable: %{http_code}\n" -u "$AUTH" "$GEOSERVER_PUBLIC_URL/rest/workspaces/$W/datastores/pg/featuretypes.json?list=available"
echo "capabilities: $(curl -s -G --data-urlencode service=WMS --data-urlencode version=1.3.0 --data-urlencode request=GetCapabilities "$GEOSERVER_PUBLIC_URL/$W/wms" | grep -m1 -o WMS_Capabilities)"

A fresh deploy prints health: 200, anonymous rest: 401, workspace: 201, postgis store: 201, postgis reachable: 200 and capabilities: WMS_Capabilities.

Why Deploy GeoServer on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying GeoServer on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

  • Agent-ready - generated admin password, contract block and one working script; no setup form.
  • PostGIS included - a spatial database next to GeoServer on the private network, not exposed to the internet.
  • State survives redeploys - the data directory and the database each sit on a volume.
  • Credentials from variables - the admin password is applied from the environment on every boot.
  • Correct public URLs - proxy headers are on, so capabilities documents point at your HTTPS domain, not an internal address.

Common Use Cases

  • Web maps for a front end - serve WMS or WMTS tiles to Leaflet, OpenLayers or MapLibre.
  • Spatial APIs for agents and apps - publish PostGIS tables as WFS features and query them over HTTP.
  • A shared map server for a team - publish shapefiles, GeoTIFFs and GeoPackages with styles managed in the admin UI.

Dependencies for GeoServer Hosting

  • PostgreSQL 18 with PostGIS 3.6 and one Railway volume, created by the template.
  • One Railway volume for the GeoServer data directory, created by the template.

Deployment Dependencies

Implementation Details

VariablePurpose
GEOSERVER_ADMIN_PASSWORDPassword of the admin user, generated per deploy.
GEOSERVER_PUBLIC_URLHTTPS address for clients outside Railway.
GEOSERVER_PRIVATE_URLAddress on the private network, for services in the same project.
CSRF_WHITELISTPublic domain allowed to post to the admin UI behind Railway's proxy.
POSTGIS_HOSTPrivate hostname of the postgis service.
POSTGIS_PASSWORDPassword of the postgres user in the database, generated per deploy.

The start command sets the admin user, the heap sizes and the proxy-header switches, then runs the image's own entrypoint. GeoServer is not wired to PostGIS automatically because a store needs a workspace of your choosing; the contract script shows the REST call that creates one.


Template Content

More templates in this category

View Template
Rocky Linux
Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀

codestorm
50
View Template
Foundry Virtual Tabletop
A Self-Hosted & Modern Roleplaying Platform

Lucas
71
View Template
Letta Code Remote
Run a Letta Code agent 24/7. No inbound ports, just deploy.

Letta
51