Building a Production-Ready PostgreSQL High Availability Cluster with Patroni, Etcd, and PgBouncer on 3 Budget VPS
Introduction: The Imperative of Database High Availability on a Budget
In the modern digital economy, data is the most critical asset of any enterprise. System downtime translates directly to financial loss, damaged reputation, and compromised user trust. For database administrators and infrastructure engineers, ensuring that PostgreSQL—one of the world's most advanced open-source relational databases—remains continuously available is a top priority.
Traditionally, achieving true High Availability (HA) required expensive enterprise-grade hardware or costly cloud-native managed solutions. However, by combining powerful open-source orchestration tools, businesses can build a robust, self-healing, production-ready PostgreSQL HA cluster on just three budget Virtual Private Servers (VPS). This guide walks you through the practical architecture and deployment of an HA cluster using Patroni for orchestration, Etcd for distributed consensus, and PgBouncer for efficient connection pooling.
1. The Architectural Blueprint: Component Breakdown
To design a system that tolerates hardware failures without human intervention, we must eliminate all Single Points of Failure (SPOFs). Our architecture relies on a specialized three-node topology, utilizing three distinct open-source technologies working in harmony.
The Core Components
- PostgreSQL (The Database Engine): Provides the underlying relational database management system, utilizing streaming replication to sync data from the Primary node to Standby nodes.
- Patroni (The Orchestrator): A Python-based template that transforms PostgreSQL into a dynamic, self-healing cluster. Patroni monitors the local database instance and updates the Distributed Configuration Store (DCS).
- Etcd (The Distributed Configuration Store): A strongly consistent, distributed key-value store. Etcd manages the cluster state and handles leader election. A minimum of three nodes is mathematically required to achieve a quorum (majority agreement), preventing split-brain scenarios where two nodes simultaneously believe they are the leader.
- PgBouncer (The Connection Pooler): A lightweight connection pooler for PostgreSQL. It reduces overhead by managing a pool of reusable connections and routes client traffic appropriately depending on whether a node is a primary or replica.
Node Allocation and IP Mapping
For this practical deployment, we allocate three budget VPS instances running a stable Linux distribution like Ubuntu 22.04 LTS or Debian 12:
- Node 1 (vps-01): IP
10.0.0.11— Runs Etcd, Patroni, PostgreSQL, PgBouncer - Node 2 (vps-02): IP
10.0.0.12— Runs Etcd, Patroni, PostgreSQL, PgBouncer - Node 3 (vps-03): IP
10.0.0.13— Runs Etcd, Patroni, PostgreSQL, PgBouncer
2. Step-by-Step Deployment Guide
Before proceeding, ensure all three nodes can communicate over a secure private network, have SSH keys configured, and have firewall rules adjusted to allow traffic on ports 2379/2380 (Etcd), 8008 (Patroni), 5432 (PostgreSQL), and 6432 (PgBouncer).
Step 2.1: Setting Up the Etcd Cluster
First, update system repositories and install Etcd on all three nodes:
sudo apt-get update && sudo apt-get install -y etcd-server etcd-client
Modify the /etc/etcd/etcd.yml configuration file on each node. The configuration must point to the specific node's IP and declare the peer addresses of the other two instances. Below is an example snippet for vps-01:
name: 'vps-01'
data-dir: '/var/lib/etcd/vps-01.etcd'
listen-peer-urls: '[http://10.0.0.11:2380](http://10.0.0.11:2380)'
listen-client-urls: '[http://10.0.0.11:2379](http://10.0.0.11:2379),[http://127.0.0.1:2379](http://127.0.0.1:2379)'
initial-advertise-peer-urls: '[http://10.0.0.11:2380](http://10.0.0.11:2380)'
advertise-client-urls: '[http://10.0.0.11:2379](http://10.0.0.11:2379)'
initial-cluster: 'vps-01=[http://10.0.0.11:2380](http://10.0.0.11:2380),vps-02=[http://10.0.0.12:2380](http://10.0.0.12:2380),vps-03=[http://10.0.0.13:2380](http://10.0.0.13:2380)'
initial-cluster-token: 'etcd-postgres-cluster-token'
initial-cluster-state: 'new'
Restart the Etcd service across all nodes and verify the cluster health using the command-line utility:
sudo systemctl restart etcd
etcdctl endpoint health --cluster
Ensure all endpoints return a healthy status before proceeding to the database layer.
Step 2.2: Installing and Configuring Patroni
Install PostgreSQL and Patroni dependencies. It is critical to stop and disable the default PostgreSQL service, as Patroni must have full operational custody over starting, stopping, and bootstrapping the database instances.
sudo apt-get install -y postgresql-15 patroni
sudo systemctl stop postgresql
sudo systemctl disable postgresql
Create the Patroni configuration file at /etc/patroni/patroni.yml. This file instructs Patroni how to interface with Etcd, sets up the bootstrap parameters for PostgreSQL replication, and configures authentication credentials. A condensed structure for the file includes:
scope: postgres-ha-cluster
namespace: /service
name: vps-01
etcd3:
hosts:
- 10.0.0.11:2379
- 10.0.0.12:2379
- 10.0.0.13:2379
restapi:
listen: 10.0.0.11:8008
connect_address: 10.0.0.11:8008
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
postgresql:
use_pg_rewind: true
use_slots: true
initdb:
- auth-host: md5
- auth-local: trust
- encoding: UTF8
pg_hba:
- host replication replicator 10.0.0.0/24 md5
- host all all 0.0.0.0/0 md5
postgresql:
listen: 10.0.0.11:5432
connect_address: 10.0.0.11:5432
data_dir: /var/lib/postgresql/15/main
bin_dir: /usr/lib/postgresql/15/bin
authentication:
replication:
username: replicator
password: 'SecureReplicationPassword'
superuser:
username: postgres
password: 'SecureMasterPassword'
Apply the configuration on all nodes, adjusting the node names and local IP addresses appropriately. Start the Patroni daemon service:
sudo systemctl start patroni
sudo systemctl enable patroni
To inspect the status of your newly formed cluster, utilize the Patroni control command line tool:
patronictl -c /etc/patroni/patroni.yml list
You should see one node dynamically elected as the Leader (Primary) with a running status, while the remaining two nodes are provisioned automatically as read-only Sync Standbys via streaming replication.
Step 2.3: Implementing PgBouncer for Load Balancing and Connection Pooling
Applications should not talk directly to PostgreSQL IPs in an HA cluster, because the Primary node can change during a failover. Instead, we use PgBouncer accompanied by automated routing logic.
Install PgBouncer on each node:
sudo apt-get install -y pgbouncer
Configure /etc/pgbouncer/pgbouncer.ini to manage connections effectively. To make PgBouncer route traffic seamlessly to whichever node is currently the leader, you can leverage Patroni's built-in HTTP REST API endpoints (specifically /primary and /replica) combined with an infrastructure load balancer like HAProxy or keepalived, or utilize automated script integrations that dynamically rewrite the PgBouncer configuration file upon failover events.
3. Validating Automatic Failover and Resilience
The true metric of a High Availability system is how it responds to unexpected infrastructure failures. We can conduct a controlled failure simulation to witness Patroni and Etcd in action.
- Log into your application dashboard and run a continuous write loop to monitor database availability.
- Simulate a catastrophic hardware fault on your Primary database instance by abruptly stopping the Patroni service or shutting down the virtual machine entirely:
sudo systemctl stop patroni - Monitor the logs on the remaining nodes. Within seconds, the Etcd lease will expire due to the lack of heartbeats.
- The remaining healthy nodes will trigger an election. The node with the most up-to-date transaction log (LSN) will be safely promoted to the new Leader.
- Patroni will issue a
pg_rewindorder to the old leader if it recovers, automatically demoting it to a Standby replica without manual intervention.
This automated sequence guarantees that data loss is minimized (or zero, depending on synchronous replication choices) and database downtime is reduced to mere seconds.
Conclusion and Best Practices for Production
Building a PostgreSQL High Availability cluster using Patroni, Etcd, and PgBouncer on 3 budget VPS instances provides an enterprise-grade solution at a fraction of the cost. However, maintaining production stability requires adhering to rigorous maintenance standards:
- Automated Backups: HA protects against hardware failure, not human error. Implement solutions like pgBackRest or WAL-G for point-in-time recovery (PITR).
- Monitoring and Alerts: Set up continuous monitoring tools like Prometheus and Grafana using the
postgres_exporterto track replication lag, disk utilization, and Etcd health. - Resource Constraints: Since budget VPS environments share underlying physical hypervisors, closely monitor CPU steal time and disk I/O bottlenecks to ensure stable latency.
By leveraging this modern open-source stack, startups and growing digital businesses can deploy resilient, reliable infrastructure capable of scaling efficiently while maintaining strict financial discipline.
