Illustrative WIUNIX Academy studio with a seated operator and PostgreSQL, Patroni and etcd concept diagram.

WIUNIX ACADEMY

PostgreSQL with Patroni: replication, quorum and safe switchover

Build a fresh PostgreSQL18/Patroni lab with TLS, three-member etcd quorum, WAL checks and a planned switchover, with explicit fencing and recovery limits.

Advanced90 min readUpdated
Make this guide yours

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Debian 12Reviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment11 editable values

THE OUTCOME

What will you have at the end?

Observe WAL replication, Patroni roles and switchover boundaries in the dedicated academy_pg18 lab.

01pg1 · 10.77.18.11:5432PostgreSQL18 + Patroni · HTTPS 8008
02pg2 · 10.77.18.12:5432WAL → standby · Patroni-selected synchronous partner
03pg3 · 10.77.18.13:5432WAL → standby · independent failure domains required
04etcd v3 · 2/3 quorumHTTPS 2379 · peer 2380 · academy_pg18
Supported operating systems and versions

OS paths are documented. Isolated executable evidence and its limits appear in the opening section; custom values are not tested deployments.

Before you begin

  • Three fresh amd64 VMs, independent console, Bash/sudo and private network 10.77.18.0/24.
  • Existing private PKI with node IP SANs, patroni client and root admin identities; protected key transfer.
  • New /srv/academy-pg18 and unused academy_pg18 DCS scope; no production data.

Build replication before trusting failover

For Linux administrators who already understand PostgreSQL access control. Build a fresh three-node PostgreSQL18 lab, let Patroni own its lifecycle, and observe WAL replay and a planned switchover. Three etcd members provide a separate majority election service. This is not an in-place migration, backup service, connection pooler or production HA certification.

Set public values, keep secrets out

Open the settings above, edit, Apply, then copy. Nodes pg1/pg2/pg3 use 10.77.18.11, 10.77.18.12, 10.77.18.13 inside 10.77.18.0/24. The exact management source is 10.77.18.50/32. PostgreSQL 5432, REST 8008, etcd client 2379 and peer 2380 must be distinct. A dedicated absolute root /srv/academy-pg18 holds this lab only; never choose an existing database, mount root or shared application directory. Cluster academy_pg18 must also be new in DCS. Inspect real routes, ownership, symlinks and listeners: syntax checks cannot establish these facts. Custom values and custom progress stay in sessionStorage for this tab/language/article/environment; default progress uses localStorage. No values enter URLs, shared links or server writes. Never enter passwords, tokens or keys. Custom examples are not executed tests.

Three voters are not three independent hosts

PostgreSQL sends physical WAL from one primary to two standbys. All data nodes use the same major version, architecture, extensions and tablespace layout. Keep minor releases aligned. Patroni coordinates roles through an etcd v3 lease; it does not replicate data itself. A three-member DCS needs two votes and tolerates one member failure. A two-member DCS also needs two votes and is not a useful one-failure design.

Use three independently failing VMs/hosts and storage paths; put DCS voters across genuine failure domains. Co-locating one DCS voter on each database VM is a small lab topology, not protection from a shared hypervisor, disk, rack, power supply or network partition. Separate DCS hosts may be appropriate in production. Clients need a role-aware endpoint and reconnect/retry policy; this guide uses explicit node addresses and does not provision HAProxy or a floating IP.

Choose the cost of unavailable writes

The example enables Patroni synchronous_mode and synchronous_mode_strict with one synchronous standby. Acknowledged commits normally wait for a standby WAL flush; losing the synchronous partner blocks writes until a suitable replacement is selected. Latency crosses the replication link. Do not hand-edit synchronous_standby_names behind Patroni.

Asynchronous mode trades durability for availability: the promotion lag threshold is not a zero-loss promise because leader WAL position is sampled. Strict synchronous mode can sacrifice availability; forced promotion, clients overriding synchronous_commit, a cancelled commit with uncertain outcome, correlated storage loss and broken fencing still matter. Define RPO (acceptable lost work) and RTO (measured recovery interval) with the application. The ttl is not your measured RTO.

01

LET’S BUILD

Inspect only fresh, disposable VMs

On each of three lab VMs, retain provider console access. Stop if PostgreSQL, Patroni, etcd, the dedicated units or the root already exist. Do not stop an existing database to make this procedure fit. Ports must be unused and the selected private addresses must belong to these VMs. Review host firewall ownership without changing it here.

Bash

Disposable database VM; Bash as administrator

set -eu
cat /etc/os-release
dpkg --print-architecture
python3 --version
ip -br address
sudo ss -lntup
dpkg-query -W postgresql-common patroni etcd-server 2>/dev/null || true
systemctl list-unit-files 'postgresql*' 'patroni*' 'etcd*' 'academy-pg*'
test ! -e '/srv/academy-pg18'
test ! -L '/srv/academy-pg18'
test ! -e /etc/systemd/system/academy-pg-patroni.service
test ! -e /etc/systemd/system/academy-pg-etcd.service
How do I know it worked?

Unused paths/ports and no existing database/DCS ownership; otherwise stop.

Review the actual OS and package path

Reviewed upstream stable: PostgreSQL18.6, Patroni4.1.5, etcd3.7.2; PostgreSQL19 RC1 is not selected. PGDG supports Ubuntu26.10 stonking on amd64 only, Ubuntu26.04 resolute/24.04 noble LTS, and Debian13 trixie/12 bookworm. This walkthrough deliberately targets amd64 on every selection. Ubuntu26.10 ships Python3.14 and Patroni4.1.4; its PGDG amd64 index has no Patroni package. Other selected PGDG indexes list Patroni4.1.5. Ubuntu26.04 uses Python3.14,24.04 uses3.12, Debian13 uses3.13 and12 uses3.11. Package patches and security candidates matter; upstream classifiers alone are not a distribution support guarantee. Never use a foreign suite or PG19 binaries as fallback.

Explore all tutorials ↗