Deploy Postgres HA [Oct '26]

Self-healing PostgreSQL 18: automated failover + read replica. [Oct '26]

Deploy Postgres HA [Oct '26]

Just deployed

/data

Just deployed

/data

Just deployed

/var/lib/pgadmin

Just deployed

/data

๐ŸŸข Actively maintained โ€” last updated Oct '26.

๐Ÿ˜ Postgres HA Read Replica - Premium Railway Template

A highly available PostgreSQL cluster with automated failover and a streaming read replica, powered by pg_auto_failover and deployed as a single Docker image across three Railway services โ€” plus a pgAdmin web UI with both nodes pre-registered. One custom image, four services: monitor, primary, replica, pgAdmin. The monitor watches the primary; if it dies, the replica is promoted automatically and the old primary rejoins as a standby when it comes back. Database traffic stays on Railway's private network.

> ๐Ÿ”„ Actively maintained โ€” latest update 2026-10-28: PostgreSQL 18 + pg_auto_failover 2.2; private-network app connections fixed. History: CHANGELOG.md.


Deploy and Host Postgres HA Read Replica

Deploying this template provisions a three-service PostgreSQL high-availability formation plus a pgAdmin UI service in your Railway project:

ComponentTypeSource / ImagePersistent VolumePublic URLDescription
pgaf-monitorServicewotonews/postgres-ha:v0.3.0Yes (/data)Nopg_auto_failover monitor โ€” the witness that arbitrates failover.
pgaf-primaryServicewotonews/postgres-ha:v0.3.0Yes (/data)NoThe primary Postgres node โ€” accepts all writes and reads.
pgaf-replicaServicewotonews/postgres-ha:v0.3.0Yes (/data)NoThe standby node โ€” hot streaming replica, promoted on failover.
pgadminServicewotonews/pgadmin-ha:v9.18-2Yes (/var/lib/pgadmin)Generate oneBrowser UI with both cluster nodes pre-registered (primary (writes) / replica (reads)).

pgAdmin: one-click cluster insight

Generate a public domain on the pgadmin service and open it (credentials below). Both nodes appear in the server tree labeled by role โ€” browse schemas, run SQL, watch sessions. The cluster uses private-network trust, so pgAdmin connects without a database password; role labels follow each host's current role and swap after failover.

Login credentials

pgAdmin web UI (the only service meant to be public):

URLthe pgadmin service's public domain (https://)
Username / Emailadmin@template.local (fixed placeholder โ€” not a real inbox)
Passwordthe generated PGADMIN_DEFAULT_PASSWORD variable โ€” pgadmin service โ†’ Variables tab

The database nodes have no login screen and no password by design: pgaf-primary / pgaf-replica / pgaf-monitor are private-network only (trust authentication inside Railway's private network โ€” nothing is exposed to the internet). Connect your app services with postgresql://pgaf-primary.railway.internal:5432/postgres (writes) or postgresql://pgaf-replica.railway.internal:5432/postgres (reads).

> The pgAdmin password is generated fresh at deploy time via ${{secret(20)}} > and applies on first boot only; change it inside pgAdmin afterwards.

About Hosting Postgres HA Read Replica on Railway

The three cluster services run the same image and self-organize: roles derive from Railway-injected env (RAILWAY_SERVICE_NAME โ†’ monitor vs node, RAILWAY_PRIVATE_DOMAIN โ†’ node identity). The first node to join becomes primary; the second clones it via pg_basebackup and streams WAL.

  • Automated failover: the monitor health-checks the primary every second; on failure it promotes the standby โ€” no human intervention, no DNS tricks.
  • Automatic rejoin: when the old primary returns (e.g. after a Railway redeploy), it is demoted and re-cloned as a standby. No split-brain, no manual pg_rewind.
  • Private networking: nodes talk to each other over .railway.internal hostnames on Railway's private network. Nothing is exposed to the public internet by default.

Why Deploy Postgres HA Read Replica?

  • Sleep-at-night durability: a dead primary costs you seconds of availability, not your database.
  • Read scaling: point reporting/analytics traffic at pgaf-replica.railway.internal to keep the primary free for writes.
  • Railway-native: one image, three services, standard volumes โ€” scale the replica tier by duplicating the pgaf-replica service.
  • No lock-in: plain PostgreSQL 18 โ€” pg_dump and walk away.

Common Use Cases

  • Production Postgres for APIs and web apps that cannot tolerate database downtime.
  • Offloading heavy read/reporting queries to a hot standby.
  • Zero-downtime maintenance windows (fail over, patch, fail back).
  • A resilient Postgres backend for queues, analytics, or stateful workers.

Dependencies for Deploying Postgres HA Read Replica

Deployment Dependencies

  • A Railway account (the template deploys three services + three volumes).
  • Your application services must live in the same Railway project and environment to reach the cluster over the private network.
  • No public port is exposed. To connect from inside Railway:
    • Writes: postgresql://postgres@pgaf-primary.railway.internal:5432/postgres
    • Reads: postgresql://postgres@pgaf-replica.railway.internal:5432/postgres

๐Ÿ“ Architecture & Components

graph TD
    App[Your App Services<br>same Railway project] --&gt;|writes| Primary[pgaf-primary]
    App --&gt;|reads| Replica[pgaf-replica]
    Monitor[pgaf-monitor] &lt;--&gt;|health checks + failover arbitration| Primary
    Monitor &lt;--&gt;|health checks + promotion| Replica
    Primary --&gt;|WAL streaming| Replica
    Primary --- V1[(volume /data)]
    Replica --- V2[(volume /data)]
    Monitor --- V3[(volume /data)]

How failover works

  1. Monitor misses health checks from the primary (default: ~10s of failures).

  2. Monitor promotes the standby โ€” it becomes the new primary.

  3. Clients reading pgaf-primary.railway.internal should reconnect to pgaf-replica.railway.internal. The recommended app pattern is the multi-host connection string (transparent failover):

    postgresql://postgres@pgaf-primary.railway.internal:5432,postgres@pgaf-replica.railway.internal:5432/postgres?target_session_attrs=read-write
    
  4. Old primary comes back โ†’ demoted and re-cloned as the new standby. Roles converge without manual action.

Scaling out

Duplicate the pgaf-replica service (same image, same variables) to add more standbys. Each new node clones the current primary and joins the formation.


โš™๏ธ Configuration

Zero-configuration cluster. The pg_auto_failover services ship no variables at all โ€” everything self-wires from Railway-injected environment variables. The only template variable is the generated pgAdmin password:

ServiceVariableDefaultDescription
pgadminPGADMIN_DEFAULT_PASSWORDgenerated (${{secret(20)}})Login password for admin@template.local.

Cluster derivation (injected, not configurable through the card):

Derived fromUsed for
RAILWAY_SERVICE_NAMERole: the pgaf-monitor service runs the monitor; every other service joins as a Postgres node.
RAILWAY_PRIVATE_DOMAINThis node's private DNS name (NODE_HOST).

Optional overrides (for renamed services or local Docker runs):

VariableDefaultDescription
NODE_ROLEderivedForce the role: monitor, primary, or secondary (primary/secondary behave identically โ€” the formation assigns real roles).
NODE_HOSTderivedOverride this node's DNS name.
MONITOR_SERVICEpgaf-monitorWhich service name runs the monitor (also derives MONITOR_HOST).
MONITOR_HOSTpgaf-monitor.railway.internalOverride the monitor's DNS name.
PGDATA/data/pgdataPostgres data directory (on the volume).
PGPORT5432Postgres port.
WAIT_TIMEOUT300Seconds a node waits for the monitor before exiting (restart policy retries).

> Renaming services? Set NODE_ROLE=monitor on the monitor and > MONITOR_HOST= on the nodes โ€” otherwise keep names as-is.

๐Ÿ”’ Security model

  • Inter-node auth is pg_auto_failover trust mode, scoped to Railway's private network โ€” unreachable from the public internet.
  • Do not attach a public TCP domain to these services: that would bypass network isolation.
  • To add password auth, fork the image and manage PGUSER/passwords via pg_autoctl configuration (--auth scram-sha-256).

๐Ÿš€ Setup & Deployment Guide

  1. Click Deploy on the template card โ€” Railway provisions the services with their volumes and variables.
  2. Wait for all deployments to report success (~1โ€“2 minutes; the replica clones the primary on first boot).
  3. Connect apps to pgaf-primary.railway.internal:5432 (writes) and pgaf-replica.railway.internal:5432 (reads).
  4. Optional โ€” verify the formation (railway ssh -s pgaf-monitor): pg_autoctl show state should list one node primary and one secondary.

Local development: git clone &amp;&amp; cd postgres-ha &amp;&amp; cp .env.example .env.


๐Ÿงช End-to-End (E2E) Testing

bash e2e/run-tests.sh

Validates structure, marketplace limits, the manifest, volume wiring, and Dockerfile/entrypoint contracts; with a Docker daemon it also builds the cluster image and proves a three-node formation converges, streams, and promotes the replica after a hard primary kill. CI runs the suite on every push.


๐Ÿ“ Operational notes

  • Cost: three always-on services + volumes; the monitor fits a small plan.
  • Backups still matter: HA is not backup. pg_dump against the replica avoids load on the primary.
  • Upgrades: bump the image tag in railway.json, redeploy services one at a time โ€” pg_auto_failover fails over during primary redeploys automatically.

Template Content

More templates in this category

View Template
RustFS | (Just Updated) S3 Object Store, Public URL and Keys Set
S3-compatible object store, keys and public URL preset, volume kept

SuperSlowSloth
0
View Template
TimescaleDB
A PostgreSQL-based time-series database for analytics on Railway.

Rakit
1
View Template
Garage S3 Storage
Ultra-light S3 server: fast, open-source, plug-and-play.

PROJETOS
8