Railway

Deploy Postgres HA Read Replica

Highly available PostgreSQL with automated failover and a read replica.

Deploy Postgres HA Read Replica

Just deployed

/data

Just deployed

/data

Just deployed

/data

๐Ÿ˜ 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.

> The short version: one custom image, three services (monitor, primary, > replica). 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. All traffic stays on Railway's private network.


Deploy and Host Postgres HA Read Replica

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

ComponentTypeSource / ImagePersistent VolumePublic URLDescription
pgaf-monitorServicewotonews/postgres-ha:v0.1.0Yes (/data)Nopg_auto_failover monitor โ€” the witness that arbitrates failover.
pgaf-primaryServicewotonews/postgres-ha:v0.1.0Yes (/data)NoThe primary Postgres node โ€” accepts all writes and reads.
pgaf-replicaServicewotonews/postgres-ha:v0.1.0Yes (/data)NoThe standby node โ€” hot streaming replica, promoted on failover.

About Hosting Postgres HA Read Replica on Railway

All three services run the same image and self-organize: roles are derived from Railway-injected environment variables (RAILWAY_SERVICE_NAME โ†’ monitor vs node, RAILWAY_PRIVATE_DOMAIN โ†’ node identity). The first Postgres node to join the formation becomes the primary; the second clones it with pg_basebackup and streams WAL continuously.

  • 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: it's plain PostgreSQL 16 โ€” dump, pg_dump, and walk away whenever you like.

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 pattern for app connections is the multi-host connection string:

    postgresql://postgres@pgaf-primary.railway.internal:5432,postgres@pgaf-replica.railway.internal:5432/postgres?target_session_attrs=read-write
    
  4. Old primary comes back โ†’ pg_auto_failover demotes and re-clones it 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 on Railway. The template ships no required variables โ€” deployers click Deploy and everything self-wires from Railway-injected environment variables:

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 authentication uses pg_auto_failover's 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. Private-network access only.
  • To add password auth, set --auth scram-sha-256 behavior by forking the image and managing PGUSER/passwords via pg_autoctl configuration.

๐Ÿš€ Setup & Deployment Guide

Deploy from the marketplace

  1. Click Deploy on the template card โ€” Railway provisions the three services with their volumes and variables.
  2. Wait for all three deployments to report success (~1โ€“2 minutes; the replica clones the primary on first boot).
  3. Connect your app services to pgaf-primary.railway.internal:5432 (writes) and pgaf-replica.railway.internal:5432 (reads).

Verify the formation

From any of the three services (railway ssh -s pgaf-monitor):

pg_autoctl show state
# expect: one PRIMARY node and one SECONDARY node in state "primary" / "secondary"

Local development

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

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

bash e2e/run-tests.sh

Validates directory structure, marketplace publish limits, the three-service manifest, role variable wiring, and entrypoint/Dockerfile syntax. CI runs the same suite on every push.


๐Ÿ“ Operational notes

  • Cost: three always-on services + volumes. The monitor is tiny; consider a small plan for it.
  • Backups still matter: HA is not backup. Use pg_dump against the replica to avoid load on the primary.
  • Upgrades: pin/ 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
Garage S3 Storage
Ultra-light S3 server: fast, open-source, plug-and-play.

PROJETOS
8
View Template
Redis
Self Host Latest Redis with Railway

2
View Template
EasyImg
Simple self-hostable Nuxt.js personal image hosting system.

Muhammad Bilal
0