How Many Connections Can PostgreSQL Handle, and When Do You Need PgBouncer?
When a service connects to PostgreSQL, the database opens a server process to handle that client’s queries. PostgreSQL’s max_connections setting limits how…
When a PostgreSQL server fails, Patroni coordinates the choice of a replacement, and HAProxy directs new client connections to it. Patroni manages the PostgreSQL servers and coordinates promotion. etcd stores shared cluster state that Patroni uses to agree on the leader. HAProxy sends client connections to the current primary. This setup supports automatic failover for a cluster with one read/write server and one or more replicas.
A replica receives changes from the primary through PostgreSQL streaming replication, which sends write-ahead log (WAL) records between servers. A replica can take over after a failure, but the result depends on how much data it received before the primary stopped. The setup therefore requires decisions about acceptable data loss, recovery time, and whether write availability or durability takes priority. The Patroni overview describes the components and their roles. A working deployment also needs tested failover behavior and secure connections, along with backups.
Start by deciding how many PostgreSQL servers and etcd members to run, where to place them, and which failures the cluster must survive. PostgreSQL nodes store the database and serve queries. etcd members maintain a distributed configuration store, or DCS, that Patroni uses to coordinate cluster state and leadership. These are separate layers: adding PostgreSQL servers does not provide the etcd quorum Patroni needs. An etcd quorum is the majority of members that must be available to agree on cluster state.
A two-node PostgreSQL cluster can promote its replica after a primary failure, but it has no standby left until the failed server rejoins. Patroni describes this as a common starting arrangement. For etcd, Patroni recommends three or five members. Three etcd members need two to reach a majority and tolerate one member failure; five need three and tolerate two, according to the etcd quorum guidance. When practical, place members where a single failure is less likely to affect all of them. Cross-region communication adds network latency and uses bandwidth.
Next, choose asynchronous or synchronous replication. PostgreSQL streaming replication is asynchronous by default: the primary does not wait for a replica to confirm every commit. That keeps writes from waiting on replica acknowledgment, but a promoted replica can lack transactions that had already committed on the failed primary. Patroni’s maximum_lag_on_failover setting limits how far behind a replica can be and still qualify for promotion. It does not guarantee zero data loss; the practical loss also depends on WAL generated since Patroni last sampled the primary’s position and on the ttl timing. The Patroni replication-mode documentation explains these limits.
Synchronous replication waits for a standby acknowledgment for transactions covered by the selected policy. This improves durability at the cost of added write latency, and writes can become unavailable when the required standby cannot respond. Patroni recommends at least three PostgreSQL data nodes when the goal is to preserve write availability after one host fails in synchronous mode. No replication setting guarantees protection against every combination of failures; set the policy according to the data-loss tolerance and availability needs of the workload.
Install PostgreSQL, Patroni, etcd, and HAProxy using versions tested together. Patroni’s installation instructions describe package and Python installation options, including an example with PostgreSQL driver and etcd v3 support. Pin versions for deployments instead of installing whichever package version is latest.
Configure etcd first. Each member needs a unique name. It also needs peer URLs to communicate with other etcd members, plus client URLs for clients such as Patroni. The etcd 3.5 clustering guide explains the distinction between peer and client addresses and the cluster bootstrap process. Restrict network access to the hosts and paths that need it, and configure authentication and TLS to match the deployment’s security requirements.
Configure Patroni to use etcd’s v3 protocol through its etcd3 settings. Do not copy older examples that enable etcd’s v2 API without checking them against the target etcd release. Patroni warns that v2 and v3 store keys separately, so changing the protocol section does not make existing cluster state visible across protocols.
Give every Patroni-managed node the same cluster scope and a unique name. The scope identifies the cluster; the name identifies a member. In each node’s configuration, point etcd3 to the etcd client endpoints, set a reachable restapi address, and specify PostgreSQL’s listen address, data directory, and authentication settings. The REST API lets Patroni expose member health and role information to other cluster components.
Use the bootstrap section to define the initial PostgreSQL cluster settings and initialization behavior. Patroni writes the initial dynamic settings to the DCS when it initializes the first member. Editing bootstrap.dcs later does not reapply those settings; change cluster-wide dynamic settings through patronictl edit-config or the REST API instead. The Patroni YAML configuration reference describes these settings.
Set up separate credentials for application connections, replication, and Patroni’s administrative operations. Patroni advises applications to connect as a non-superuser. If Patroni will use pg_rewind to bring a former primary back into the cluster, enable data checksums during initialization and/or enable wal_log_hints, as described in its dynamic configuration documentation.
Configure HAProxy with a PostgreSQL TCP frontend for clients and a backend containing the PostgreSQL nodes. For a write endpoint, HAProxy must check which node is the primary. Patroni exposes role-aware health endpoints, including /primary; HAProxy can query that endpoint on each node’s Patroni REST API port while sending database traffic to PostgreSQL’s port. Keep the client-facing database connection separate from the REST health-check connection. The Patroni HAProxy example demonstrates this approach.
A PostgreSQL-native HAProxy check can confirm that a PostgreSQL server responds to a startup message, but it does not distinguish a primary from a replica. HAProxy’s health-check documentation describes PostgreSQL checks; use a role-aware check for a primary-only write pool.
A read pool requires a separate routing policy. A replica can accept reads while lagging behind the primary, so clients that read from replicas can receive stale results. PostgreSQL’s high-availability overview describes this effect in load-balanced replication.
One HAProxy instance gives clients a stable database address, but that proxy is still a single point of failure. Production deployments need a redundant proxy tier and a resilient way for clients to reach it. The specific design depends on the network and hosting environment.
Before directing production traffic to the cluster, confirm that Patroni reports the expected member roles and that clients can connect through HAProxy. Then test controlled failures in a nonproduction environment: stop the primary, stop a replica, remove an etcd member, and stop the HAProxy process or host. Observe whether Patroni promotes an eligible replica and whether HAProxy routes new connections to it.
Failover does not preserve existing database sessions. Clients need to reconnect and retry, and operators need to check how the application handles interrupted transactions. A stable endpoint simplifies reconnecting after a role change, but it does not mean uninterrupted sessions or guaranteed zero downtime. Patroni’s overview recommends testing component failures to see how the cluster behaves.
High availability also does not replace backup and disaster-recovery planning. Keep backups and WAL archives according to recovery requirements, and test restores. PostgreSQL’s standby and WAL guidance covers WAL archiving and standby recovery, including the need to keep archive access available when the primary is down.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com