Deploy PostgreSQL

Run PostgreSQL 18 on Railway with Docker, volume-ready defaults.

Deploy PostgreSQL

Just deployed

PostgreSQL for railway.app

Template Header

Deploy PostgreSQL 18 on Railway with the official Docker image. This template provisions a ready-to-run relational database for your application data, analytics, or as a shared backing store for other services in your project. It includes sensible defaults, a built-in health check, and guidance for attaching persistent storage so your data survives redeploys.

Deploy on Railway

🏗️ Architecture

flowchart LR
    Client(["📦 App / Client"]) -->|"PostgreSQL Protocol"| Proxy["Railway TCP Proxy"]
    Proxy -->|"$PORT → 5432"| App["Container\npostgres:18-alpine"]
    App --> Volume[("Volume\n/var/lib/postgresql/data")]

Environment

VariableRequiredDescription
POSTGRES_PASSWORDYesPassword for the postgres user

Optional

VariableDescription
POSTGRES_USERDefaults to postgres
POSTGRES_DBDefaults to postgres
POSTGRES_INIT_DUMP_URLHTTPS URL to a .sql or gzip-compressed .sql.gz dump (e.g. pg_dump --format=plain); downloaded and imported automatically, but only on the first start of an empty volume. Takes precedence over POSTGRES_INIT_DUMP_BASE64 if both are set.
POSTGRES_INIT_DUMP_BASE64Same dump, base64-encoded and pasted directly as the variable value. Ignored if POSTGRES_INIT_DUMP_URL is also set. Best for small dumps only.
POSTGRES_AUTO_UPGRADEDefaults to true. Upgrade an existing PostgreSQL 16 volume to 18 automatically on start. Set to false to keep running PostgreSQL 16 for now.
POSTGRES_UPGRADE_MODEcopy (default) or link. link uses hard links and needs almost no extra space, but there is no way back to 16 afterwards.
POSTGRES_UPGRADE_DELETE_BACKUPSet to true and redeploy to delete .pg16-backup after a successful upgrade.

[!NOTE] POSTGRES_INIT_DUMP_URL/POSTGRES_INIT_DUMP_BASE64 are only ever consulted on the very first start against a brand-new, empty volume. Once the database has initialized, both variables are silently ignored on every later restart or redeploy — even if you leave them set. The dump runs with full administrative privileges during bootstrap, so only point these at dumps from sources you trust.

Persistence

railway.toml declares requiredMountPath = "/var/lib/postgresql/data". Attach a Railway volume to that path before production traffic — Railway will prompt for it based on this setting, but it is not created automatically.

Upgrading to PostgreSQL 18

Template versions before 0.3.0 ran PostgreSQL 16. PostgreSQL 18 cannot start a PostgreSQL 16 data directory directly, so the image ships the PostgreSQL 16 binaries and upgrades an existing volume in place with pg_upgrade on the first start:

  1. PostgreSQL 16 is started once to finish crash recovery and to read the encoding, locale and checksum settings.
  2. A new PostgreSQL 18 cluster is created with the same settings in /var/lib/postgresql/data/.pg18-upgrade and filled with pg_upgrade --copy. Users, passwords, databases, extensions, pg_hba.conf and ALTER SYSTEM settings are kept.
  3. The new cluster is test-started. Only then are the directories switched. The original PostgreSQL 16 files stay in /var/lib/postgresql/data/.pg16-backup.

What to know before you update:

  • Back up first. Take a pg_dump or a Railway volume backup before you deploy the new version.
  • Disk space. The default copy mode needs about as much free space as the database already uses. If there is not enough space, the upgrade is skipped and PostgreSQL 16 keeps running. Grow the volume, or set POSTGRES_UPGRADE_MODE=link.
  • If the upgrade fails, nothing is changed: the temporary cluster is removed and the service keeps running PostgreSQL 16 with a warning in the logs. To stay on 16 on purpose, set POSTGRES_AUTO_UPGRADE=false. This compatibility mode will be removed in a later release.
  • Do not interrupt the first start. The upgrade takes roughly as long as copying the database. If the container is stopped before the switch, the next start begins again from the untouched 16 data. If it is stopped during the short switch step, the service refuses to start (marker file .pg-upgrade-switching) and changes nothing, so you can restore by hand: move the contents of .pg16-backup back into place, or complete the switch with .pg18-upgrade. With POSTGRES_UPGRADE_MODE=link, an interrupted pg_upgrade can leave the 16 cluster unusable (global/pg_control.old), so restore from your backup in that case.
  • Rolling back. Copy mode keeps the old files in .pg16-backup. To go back, move them back into place and deploy template version 0.2.x. Data written after the upgrade is not in the backup.
  • Free the space after you have checked your data: set POSTGRES_UPGRADE_DELETE_BACKUP=true and redeploy once.
  • Data path. The official PostgreSQL 18 image moved PGDATA to /var/lib/postgresql/18/docker. This template keeps PGDATA=/var/lib/postgresql/data, so the volume mount does not change.
  • New clusters created by PostgreSQL 18 have data checksums enabled. Upgraded clusters keep their previous checksum setting.
  • Only PostgreSQL 16 data can be upgraded automatically. Other versions stop with a clear error and no changes; use pg_dump/pg_restore for those.
  • See the PostgreSQL 17 and PostgreSQL 18 release notes for incompatible changes that may affect your application.

Local

docker build -t railwayapp-postgresql .
docker run --rm -e POSTGRES_PASSWORD=dev -p 5432:5432 railwayapp-postgresql

💬 Support

Questions or feedback? Visit the template forum on Railway Central Station.


Airbyte Apache Airflow CloudBeaver CodiMD Django Email Service FastAPI Flask Flowise GitLab CE Grafana Home Assistant InfluxDB MJML MongoDB Mosquitto MQTT MySQL n8n Node.js Node-RED OpenSearch Open WebUI Operately Outerbase Studio PostgreSQL Redis TYPO3 CMS


Template Content

More templates in this category

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

codestorm
51
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
52