Deploy PostgreSQL Highly Available Cluster
Self-host Postgres with zero downtime, auto failover using Patroni & etcd
Etcd
Just deployed
/var/lib/etcd
Just deployed
/var/lib/etcd
Just deployed
/var/lib/etcd
Postgres
Just deployed
/var/lib/postgresql/data
Just deployed
/var/lib/postgresql/data
Just deployed
/var/lib/postgresql/data
Just deployed
Deploy and Host PostgreSQL High Availability (Patroni) on Railway
Deploy PostgreSQL with automatic failover using Patroni on Railway. This template provisions a production-ready, 7-service HA cluster: 3 etcd nodes for distributed consensus, 3 PostgreSQL nodes managed by Patroni for streaming replication and automatic leader election, and an HAProxy load balancer that routes read-write traffic to the current primary and read-only queries to replicas.
Self-host PostgreSQL HA with Patroni on Railway to get sub-30-second automatic failover, zero-downtime maintenance windows, and horizontal read scaling — all without managing bare-metal servers or writing custom failover scripts.
Getting Started with PostgreSQL HA (Patroni) on Railway
After deployment completes, all seven services will show as active in your Railway dashboard. Patroni automatically elects a primary node (leader) and configures the other two PostgreSQL nodes as streaming replicas.
Connect to your cluster through the HAProxy TCP proxy endpoint. The read-write port (5432) routes to the current primary, while read-only port (5433) is available internally via haproxy.railway.internal:5433. Use the connection string from your Railway project variables:
psql "postgresql://railway:@monorail.proxy.rlwy.net:/railway"
Verify cluster health by checking Patroni's REST API on any postgres node at port 8008, or monitor HAProxy stats via the HTTP domain. To test failover, stop the primary node — Patroni will promote a replica within 20–30 seconds, and HAProxy will automatically reroute traffic.
About Hosting PostgreSQL HA with Patroni
Patroni is a Python-based high availability solution for PostgreSQL that automates cluster management, failover, and replication using a distributed consensus store (etcd, Consul, or ZooKeeper). Originally created by Zalando, it is the most widely adopted PostgreSQL HA framework in production environments.
Key features:
- Automatic failover with configurable TTL and loop wait — RTO consistently 20–30 seconds
- Streaming replication with synchronous or asynchronous modes
- REST API for cluster state inspection and manual switchover/failover
- etcd-backed consensus — no split-brain scenarios
- HAProxy integration — transparent connection routing based on Patroni health checks
- Self-healing — automatically reinitializes failed nodes from the current primary
The architecture uses etcd (3-node quorum) for leader election, Patroni as a sidecar daemon on each PostgreSQL node, and HAProxy for TCP load balancing based on Patroni's health endpoints.
Why Deploy PostgreSQL HA (Patroni) on Railway
- One-click 7-service cluster — no infrastructure provisioning or manual configuration
- Private networking — all inter-service communication stays on Railway's internal network
- Persistent volumes — each PostgreSQL and etcd node gets dedicated storage
- Automatic failover — Patroni handles leader election and replica promotion
- TCP proxy — external access to PostgreSQL without VPN or SSH tunnels
Common Use Cases for Self-Hosted PostgreSQL HA
- Production databases requiring zero-downtime — SaaS platforms, e-commerce, financial systems where any downtime means lost revenue
- Read-heavy workloads — distribute SELECT queries across replicas via HAProxy's read-only port while writes go to the primary
- Compliance-sensitive data — self-hosted PostgreSQL with full control over data residency, encryption, and access
- Blue-green deployments — perform rolling PostgreSQL upgrades with Patroni switchover, zero client-side changes needed
Dependencies for PostgreSQL HA (Patroni) on Railway
- PostgreSQL Patroni (x3) —
ghcr.io/railwayapp-templates/postgres-ha/postgres-patroni:latest— PostgreSQL 16 with Patroni HA daemon - etcd (x3) —
ghcr.io/railwayapp-templates/postgres-ha/etcd:latest— Distributed key-value store for cluster consensus - HAProxy (x1) —
ghcr.io/railwayapp-templates/postgres-ha/haproxy:latest— TCP load balancer with Patroni-aware health checks
Deployment Dependencies
- Runtime: Docker (all images are pre-built)
- Docker Images: ghcr.io/railwayapp-templates/postgres-ha
- Patroni Docs: patroni.readthedocs.io
- GitHub: github.com/patroni/patroni
Hardware Requirements for Self-Hosting PostgreSQL HA with Patroni
| Component | Minimum | Recommended |
|---|---|---|
| CPU (per node) | 0.5 vCPU | 1+ vCPU |
| RAM (per postgres node) | 512 MB | 1–2 GB |
| RAM (per etcd node) | 256 MB | 512 MB |
| RAM (haproxy) | 128 MB | 256 MB |
| Storage (per postgres node) | 1 GB | 5–50 GB |
| Storage (per etcd node) | 500 MB | 1–2 GB |
| Total cluster RAM | ~2.5 GB | ~6 GB |
Self-Hosting PostgreSQL HA with Patroni Using Docker
The fastest way to self-host a Patroni cluster is with Docker Compose. Here's a minimal 3-node setup:
# docker-compose.yml (simplified)
services:
etcd-1:
image: ghcr.io/railwayapp-templates/postgres-ha/etcd:latest
environment:
ETCD_NAME: etcd-1
ETCD_INITIAL_CLUSTER: "etcd-1=http://etcd-1:2380"
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
postgres-1:
image: ghcr.io/railwayapp-templates/postgres-ha/postgres-patroni:latest
environment:
PATRONI_SCOPE: my-cluster
PATRONI_NAME: postgres-1
PATRONI_ETCD3_HOSTS: etcd-1:2379
PATRONI_SUPERUSER_USERNAME: postgres
PATRONI_SUPERUSER_PASSWORD: secretpass
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- pg1_data:/var/lib/postgresql/data
haproxy:
image: ghcr.io/railwayapp-templates/postgres-ha/haproxy:latest
environment:
POSTGRES_NODES: "postgres-1:5432:8008"
ports:
- "5432:5432"
volumes:
pg1_data:
For a production 3-node cluster, replicate the etcd and postgres services with unique names and add them to the ETCD_INITIAL_CLUSTER and POSTGRES_NODES variables.
# Connect to the cluster via HAProxy
psql -h localhost -p 5432 -U railway -d railway
How Much Does PostgreSQL HA with Patroni Cost to Self-Host?
Patroni, etcd, HAProxy, and PostgreSQL are all fully open source with no licensing fees. The entire stack is free to use in production.
On Railway, you pay only for infrastructure — compute and storage for the 7 services. A minimal cluster runs on approximately $15–25/month depending on usage. The main cost drivers are persistent storage for PostgreSQL data and RAM for the postgres nodes.
Patroni vs Repmgr vs Pgpool for PostgreSQL High Availability
| Feature | Patroni | Repmgr | Pgpool-II |
|---|---|---|---|
| Automatic failover | Yes (consensus-based) | Yes (with witness) | Yes (watchdog) |
| Split-brain prevention | Strong (etcd/Consul) | Moderate | Moderate |
| Connection pooling | No (use PgBouncer) | No | Built-in |
| Read load balancing | Via HAProxy | Manual | Built-in |
| Setup complexity | Moderate–High | Low–Moderate | Moderate |
| Production adoption | Highest | Moderate | Moderate |
| Failover time (RTO) | 20–30 seconds | 30–60 seconds | 10–30 seconds |
Patroni is the most robust choice for production HA, especially in containerized environments. Its consensus-based approach via etcd eliminates split-brain scenarios — the most dangerous failure mode in database clustering.
FAQ
What is Patroni and why use it for PostgreSQL high availability? Patroni is a Python-based HA framework that automates PostgreSQL cluster management, failover, and replication. It uses a distributed consensus store (etcd) to prevent split-brain scenarios and provides automatic leader election with sub-30-second failover times.
What does this Railway template deploy for PostgreSQL HA? This template deploys 7 services: 3 etcd nodes for distributed consensus, 3 PostgreSQL nodes managed by Patroni (1 primary + 2 replicas with streaming replication), and 1 HAProxy instance for TCP load balancing. All services communicate over Railway's private network.
Why does PostgreSQL HA need three etcd nodes on Railway? etcd requires a quorum (majority) to make decisions. With 3 nodes, the cluster tolerates 1 node failure while maintaining consensus. This prevents split-brain — where two nodes both think they're the primary — which is the most dangerous failure mode in database clustering.
How does automatic failover work with self-hosted Patroni? When the primary PostgreSQL node becomes unavailable, Patroni detects the failure via its health check loop (configurable TTL). The remaining nodes use etcd to elect a new leader. The promoted replica begins accepting writes, and HAProxy automatically routes traffic to the new primary — all within 20–30 seconds.
Can I connect my existing application to Patroni PostgreSQL on Railway? Yes. Connect through the HAProxy TCP proxy endpoint using a standard PostgreSQL connection string. Your application connects to HAProxy, which transparently routes to the current primary. No application-level changes are needed — HAProxy handles failover routing automatically.
What happens to data during a PostgreSQL failover on Railway? Patroni uses PostgreSQL streaming replication, so replicas are continuously synchronized with the primary. During failover, committed transactions on the primary are preserved on the replicas. Uncommitted transactions at the exact moment of failure may be lost (configurable with synchronous replication for zero data loss).
Template Content