PostgreSQL

High Availability with Patroni: The PostgreSQL HA Standard

Rakesh Mamidala·Founder & Lead Engineer··8 min read

Neither Data Guard Nor RAC

Oracle DBAs arrive at PostgreSQL expecting a Data Guard or RAC equivalent and find neither, exactly. PostgreSQL’s core ships streaming replication but deliberately not automatic failover. Promoting a standby when the primary dies — without two nodes both thinking they’re primary — is an orchestration problem, and Patroni is the de facto standard that solves it.

The architecture

  • A Patroni agent on each node — it manages the local PostgreSQL (bootstrap, promote, demote, reconfigure) and reports health.
  • A distributed consensus store (DCS) — etcd, Consul or ZooKeeper — that holds the leader lease. Exactly one node can hold it at a time; that node is the primary.
  • A routing layer — HAProxy checking Patroni’s REST health endpoints, or a VIP — so clients always reach the current primary without knowing which node it is.
text
         clients
            │
        HAProxy ──┐  (asks each node's Patroni REST API:
            │     │   "are you the primary?")
   ┌────────┴────────┐
 node A            node B            node C
 Patroni           Patroni           Patroni
 Postgres(primary) Postgres(replica) Postgres(replica)
   └──────── DCS (etcd): holds the leader lease ────────┘

How it avoids split-brain

The DCS leader lease is the whole trick. The primary must continually renew a time-limited lease in the DCS; if it can’t (crash, network partition), the lease expires, the other nodes hold an election, and the DCS grants the lease to exactly one new leader — which Patroni then promotes. A primary that loses the DCS steps itself down. Two primaries can’t both hold a single lease, so you don’t get split-brain.

Synchronous vs asynchronous: your failover trade-off

In asynchronous mode, failover is fast but can lose the last few transactions that hadn’t reached the standby yet. In synchronous mode, a commit isn’t acknowledged until a standby has it, so failover is lossless — at the cost of higher commit latency. Patroni lets you set this policy (including maximum_lag_on_failover) so a too-stale replica is never promoted.

Rejoining a failed primary: pg_rewind

When a crashed primary comes back, it can’t just rejoin — it may have WAL the new primary never saw. pg_rewind rewinds it to the point of divergence and turns it into a standby of the new primary, without a full re-clone. Patroni runs this automatically, which is what makes recovery hands-off.

How it compares to Data Guard / RAC

Patroni is failover HA (like Data Guard fast-start failover), not shared-storage clustering. PostgreSQL is shared-nothing — there is no RAC-style shared-everything cluster — so you scale reads with replicas and get availability through promotion. For the vast majority of workloads that’s not a limitation; it’s a simpler, cheaper operational model.

Land on PostgreSQL, then make it highly available

DBMigrateAIPro gets your data onto PostgreSQL with validation you can prove; Patroni gives it Data-Guard-level failover. Free for Year 1.