What is Patroni?
Patroni is a Python-based PostgreSQL HA solution that uses a DCS (Distributed Configuration Store) such as etcd, Consul, or ZooKeeper to perform leader election and automatic failover.
Components
| Component | Role |
|---|---|
| Patroni | PostgreSQL process management + failover |
| etcd | Distributed consensus store for leader election |
| HAProxy | Application → Primary/Replica routing |
Cluster Topology
| Server | IP | Role |
|---|---|---|
| pg1 | 192.168.1.10 | PostgreSQL + Patroni + etcd |
| pg2 | 192.168.1.11 | PostgreSQL + Patroni + etcd |
| pg3 | 192.168.1.12 | PostgreSQL + Patroni + etcd |
| lb | 192.168.1.20 | HAProxy |
1. Install the etcd Cluster (all 3 nodes)
sudo apt install -y etcd# /etc/default/etcd (for pg1)
ETCD_NAME="pg1"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.1.10:2380"
ETCD_LISTEN_CLIENT_URLS="http://192.168.1.10:2379,http://127.0.0.1:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.1.10:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.1.10:2379"
ETCD_INITIAL_CLUSTER="pg1=http://192.168.1.10:2380,pg2=http://192.168.1.11:2380,pg3=http://192.168.1.12:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="pg-etcd-cluster"sudo systemctl enable --now etcd
# Check status
etcdctl endpoint status --cluster -w table2. Install PostgreSQL (all 3 nodes)
sudo apt install -y postgresql-17
sudo systemctl disable --now postgresql # Patroni will manage it instead3. Install and Configure Patroni
sudo apt install -y python3-pip
pip3 install patroni[etcd] psycopg2-binary/etc/patroni/config.yml (for pg1)
scope: pg-cluster
namespace: /db/
name: pg1
restapi:
listen: 192.168.1.10:8008
connect_address: 192.168.1.10:8008
etcd:
hosts: 192.168.1.10:2379,192.168.1.11:2379,192.168.1.12:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576 # 1MB
postgresql:
use_pg_rewind: true
parameters:
wal_level: replica
max_wal_senders: 5
max_replication_slots: 5
hot_standby: on
initdb:
- encoding: UTF8
- data-checksums
pg_hba:
- host replication replicator 192.168.1.0/24 md5
- host all all 0.0.0.0/0 md5
users:
admin:
password: AdminPass!23
options: [createrole, createdb]
postgresql:
listen: 192.168.1.10:5432
connect_address: 192.168.1.10:5432
data_dir: /var/lib/postgresql/17/main
bin_dir: /usr/lib/postgresql/17/bin
pgpass: /tmp/pgpass
authentication:
replication:
username: replicator
password: ReplPass!23
superuser:
username: postgres
password: PgPass!23
rewind:
username: rewind_user
password: RewindPass!23
tags:
nofailover: false
noloadbalance: false
clonefrom: falseFor pg2 and pg3, change only the IP addresses in name, listen, and connect_address to match each server.
systemd Service
cat > /etc/systemd/system/patroni.service << 'EOF'
[Unit]
Description=Patroni PostgreSQL HA
After=network.target etcd.service
[Service]
User=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni/config.yml
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now patroni4. Check Cluster Status
patronictl -c /etc/patroni/config.yml list+ Cluster: pg-cluster ----+----+-----------+
| Member | Host | Role | State |
+--------+----------------+---------+---------+
| pg1 | 192.168.1.10 | Leader | running |
| pg2 | 192.168.1.11 | Replica | running |
| pg3 | 192.168.1.12 | Replica | running |
+--------+----------------+---------+---------+5. Configure HAProxy (lb server)
sudo apt install -y haproxy# /etc/haproxy/haproxy.cfg
frontend pg_write
bind *:5432
default_backend pg_primary
backend pg_primary
option httpchk GET /master
http-check expect status 200
server pg1 192.168.1.10:5432 check port 8008
server pg2 192.168.1.11:5432 check port 8008
server pg3 192.168.1.12:5432 check port 8008
frontend pg_read
bind *:5433
default_backend pg_replicas
backend pg_replicas
balance roundrobin
option httpchk GET /replica
http-check expect status 200
server pg1 192.168.1.10:5432 check port 8008
server pg2 192.168.1.11:5432 check port 8008
server pg3 192.168.1.12:5432 check port 8008Use Patroni's REST API /master and /replica endpoints for health checks.
6. Manual Switchover / Failover
# Planned switchover (no data loss)
patronictl -c /etc/patroni/config.yml switchover pg-cluster
# Forced failover (emergency)
patronictl -c /etc/patroni/config.yml failover pg-clusterSummary
| Feature | Handled by |
|---|---|
| Leader election | etcd distributed consensus |
| Automatic failover | Patroni (within 30-second TTL) |
| Write routing | HAProxy → /master |
| Read distribution | HAProxy → /replica (round robin) |
Troubleshooting & FAQ
| Symptom | Cause | Fix |
|---|---|---|
| Failover does not occur | etcd quorum lost (2 of 3 nodes down) | Recover etcd nodes → Patroni resumes automatically once majority is restored |
| Split-brain concern | Network partition | Patroni prevents this via DCS (etcd) leader lease — keep etcd as the single source of truth |
patronictl list is empty | namespace/scope mismatch in config.yml | Verify that namespace and scope are identical on all nodes |
| Slow leader promotion | ttl/loop_wait set too high | Recommended: ttl: 30, loop_wait: 10, retry_timeout: 10 |
Q. What are the respective roles of Patroni and etcd for PostgreSQL? A. Patroni is the agent attached to each PostgreSQL node that manages state and executes failover. etcd is the source of truth (DCS) that stores “who is the leader” via distributed consensus. Leader election and automatic failover only work while an etcd majority is alive.
Q. Why do I need etcd if I already have Patroni? A. A single Patroni instance only knows about its own node. For multiple nodes to agree that there is exactly one leader, you need an external distributed store — that is etcd (or Consul / ZooKeeper). Without etcd you cannot prevent split-brain.
AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.