Set up PostgreSQL 17 streaming replication with PgBouncer connection pooling and load balancing

Advanced 45 min Apr 29, 2026 26 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure PostgreSQL 17 streaming replication with automatic failover, PgBouncer connection pooling for performance optimization, and HAProxy load balancing for high availability database architecture.

Prerequisites

  • Root or sudo access
  • At least 2 servers with 4GB RAM each
  • Network connectivity between servers
  • Basic PostgreSQL knowledge

What this solves

PostgreSQL streaming replication with PgBouncer connection pooling creates a high-availability database setup that can handle read/write splitting and automatic failover. This configuration provides connection pooling to reduce database load, read replicas for performance scaling, and load balancing for optimal resource utilization.

Step-by-step configuration

Update system packages

Start by updating your package manager to ensure you get the latest versions of all components.

sudo apt update && sudo apt upgrade -y
sudo apt install -y wget ca-certificates
sudo dnf update -y
sudo dnf install -y wget ca-certificates

Install PostgreSQL 17

Install PostgreSQL 17 from the official repository to get the latest features and performance improvements.

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
sudo apt update
sudo apt install -y postgresql-17 postgresql-contrib-17
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf install -y postgresql17-server postgresql17-contrib

Initialize PostgreSQL on primary server

Initialize the PostgreSQL database cluster on your primary server. This creates the initial database structure and configuration files.

sudo systemctl start postgresql
sudo systemctl enable postgresql
sudo /usr/pgsql-17/bin/postgresql-17-setup initdb
sudo systemctl enable --now postgresql-17

Configure primary PostgreSQL server

Configure the primary server for streaming replication by enabling WAL archiving and setting replication parameters.

# Connection and memory settings
listen_addresses = '*'
max_connections = 200
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 4MB
maintenance_work_mem = 64MB

WAL settings for replication

wal_level = replica max_wal_senders = 10 max_replication_slots = 10 wal_keep_size = 1GB archive_mode = on archive_command = 'cp %p /var/lib/postgresql/17/archive/%f'

Streaming replication settings

hot_standby = on wal_receiver_timeout = 60s wal_sender_timeout = 60s

Create archive directory

Create the WAL archive directory with proper permissions for PostgreSQL to store transaction logs.

sudo mkdir -p /var/lib/postgresql/17/archive
sudo chown postgres:postgres /var/lib/postgresql/17/archive
sudo chmod 700 /var/lib/postgresql/17/archive

Configure authentication for replication

Set up authentication rules to allow replication connections from replica servers.

# Database administrative login by Unix domain socket
local   all             postgres                                peer

TYPE DATABASE USER ADDRESS METHOD

local all all peer host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256

Replication connections

host replication replica 203.0.113.0/24 scram-sha-256 host all pgbouncer 203.0.113.0/24 scram-sha-256

Create replication user

Create a dedicated user for streaming replication with appropriate permissions.

sudo -u postgres psql -c "CREATE ROLE replica REPLICATION LOGIN PASSWORD 'your_secure_password';"
sudo -u postgres psql -c "CREATE ROLE pgbouncer LOGIN PASSWORD 'pgbouncer_password';"

Restart primary PostgreSQL server

Restart the PostgreSQL service to apply the configuration changes.

sudo systemctl restart postgresql

Set up replica server

On your replica server, install PostgreSQL and create a base backup from the primary server.

sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/17/main/*
sudo -u postgres pg_basebackup -h 203.0.113.10 -D /var/lib/postgresql/17/main -U replica -v -P -W -R
sudo systemctl start postgresql
sudo systemctl stop postgresql-17
sudo rm -rf /var/lib/pgsql/17/data/*
sudo -u postgres /usr/pgsql-17/bin/pg_basebackup -h 203.0.113.10 -D /var/lib/pgsql/17/data -U replica -v -P -W -R
sudo systemctl start postgresql-17

Install PgBouncer

Install PgBouncer connection pooler to manage database connections efficiently and reduce connection overhead.

sudo apt install -y pgbouncer
sudo dnf install -y pgbouncer

Configure PgBouncer on primary server

Configure PgBouncer to pool connections to the primary PostgreSQL server for write operations.

[databases]
* = host=127.0.0.1 port=5432

[pgbouncer]
listen_addr = *
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
max_db_connections = 100
reserve_pool_size = 5
reserve_pool_timeout = 3
server_reset_query = DISCARD ALL
server_check_delay = 30
server_check_query = SELECT 1
server_lifetime = 3600
server_idle_timeout = 600
log_connections = 1
log_disconnections = 1
stats_period = 60
pid_file = /var/run/postgresql/pgbouncer.pid
logfile = /var/log/postgresql/pgbouncer.log
unix_socket_dir = /var/run/postgresql

Configure PgBouncer authentication

Create the user authentication file for PgBouncer with encrypted passwords.

sudo -u postgres psql -c "SELECT usename, passwd FROM pg_shadow WHERE usename = 'pgbouncer';" | sudo tee /etc/pgbouncer/userlist.txt
sudo chown pgbouncer:pgbouncer /etc/pgbouncer/userlist.txt
sudo chmod 600 /etc/pgbouncer/userlist.txt

Configure PgBouncer on replica server

Set up PgBouncer on the replica server for read-only connections with appropriate configuration.

[databases]
* = host=127.0.0.1 port=5432

[pgbouncer]
listen_addr = *
listen_port = 6433
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
max_db_connections = 100
reserve_pool_size = 5
reserve_pool_timeout = 3
server_reset_query = DISCARD ALL
server_check_delay = 30
server_check_query = SELECT 1
server_lifetime = 3600
server_idle_timeout = 600
log_connections = 1
log_disconnections = 1
stats_period = 60
pid_file = /var/run/postgresql/pgbouncer.pid
logfile = /var/log/postgresql/pgbouncer.log
unix_socket_dir = /var/run/postgresql

Install and configure HAProxy

Install HAProxy to provide load balancing and automatic failover between primary and replica servers.

sudo apt install -y haproxy
sudo dnf install -y haproxy

Configure HAProxy load balancing

Set up HAProxy to route write operations to the primary server and read operations to replicas with health checks.

global
    daemon
    maxconn 4096
    log stdout local0
    stats socket /var/run/haproxy.sock mode 600 level admin
    stats timeout 2m

defaults
    mode tcp
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    option tcplog
    log global

listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 30s
    stats hide-version

frontend postgresql_write
    bind *:5432
    default_backend postgresql_primary

frontend postgresql_read
    bind *:5433
    default_backend postgresql_replicas

backend postgresql_primary
    balance roundrobin
    option pgsql-check user pgbouncer
    server primary 203.0.113.10:6432 check port 6432
    server replica 203.0.113.11:6433 check port 6433 backup

backend postgresql_replicas
    balance roundrobin
    option pgsql-check user pgbouncer
    server replica1 203.0.113.11:6433 check port 6433
    server primary 203.0.113.10:6432 check port 6432 backup

Start all services

Enable and start PgBouncer and HAProxy services on their respective servers.

sudo systemctl enable --now pgbouncer
sudo systemctl enable --now haproxy

Configure firewall rules

Open the necessary ports for PostgreSQL replication, PgBouncer, and HAProxy communication.

sudo ufw allow 5432/tcp
sudo ufw allow 6432/tcp
sudo ufw allow 6433/tcp
sudo ufw allow 8404/tcp
sudo firewall-cmd --permanent --add-port=5432/tcp
sudo firewall-cmd --permanent --add-port=6432/tcp
sudo firewall-cmd --permanent --add-port=6433/tcp
sudo firewall-cmd --permanent --add-port=8404/tcp
sudo firewall-cmd --reload
Security Note: Replace the example passwords with strong, unique passwords. Store them securely and consider using a secrets management system in production environments.

Verify your setup

# Check replication status on primary
sudo -u postgres psql -c "SELECT client_addr, state, sync_state FROM pg_stat_replication;"

Check replica status

sudo -u postgres psql -c "SELECT pg_is_in_recovery();"

Test PgBouncer connections

psql -h localhost -p 6432 -U postgres -c "SHOW POOLS;"

Check HAProxy stats

curl -s http://localhost:8404/stats

Test write connection through HAProxy

psql -h localhost -p 5432 -U postgres -c "SELECT now();"

Test read connection through HAProxy

psql -h localhost -p 5433 -U postgres -c "SELECT now();"

Monitoring replication health

Set up monitoring queries to track replication lag and connection pool status.

# Monitor replication lag
sudo -u postgres psql -c "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp())) AS lag_seconds;"

Check PgBouncer pool statistics

psql -h localhost -p 6432 -U pgbouncer pgbouncer -c "SHOW STATS;"

Monitor active connections

sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity WHERE state = 'active';"

Common issues

Symptom Cause Fix
Replication not starting Authentication failure Check pg_hba.conf and replica user password
PgBouncer connection refused Wrong listen address or port Verify listen_addr and listen_port in pgbouncer.ini
HAProxy backend down Health check failing Check PostgreSQL and PgBouncer are running on target servers
High replication lag Network or disk I/O bottleneck Check network connectivity and disk performance
Connection pool exhaustion Too many concurrent connections Increase default_pool_size or optimize application connection usage
Failover not working Health check misconfiguration Verify HAProxy health check settings and user permissions

Performance optimization

Fine-tune your setup for optimal performance based on your workload characteristics and server resources.

Optimize PostgreSQL settings

Adjust PostgreSQL configuration for your specific hardware and workload requirements.

# Memory settings (adjust based on available RAM)
shared_buffers = 25% of RAM
effective_cache_size = 75% of RAM
work_mem = Available RAM / max_connections / 4
maintenance_work_mem = Available RAM / 16

Connection settings

max_connections = 200 superuser_reserved_connections = 3

WAL settings for performance

wal_compression = on wal_buffers = 16MB checkpoint_completion_target = 0.9 max_wal_size = 2GB min_wal_size = 1GB

Optimize PgBouncer pools

Tune PgBouncer pool sizes based on your application connection patterns and PostgreSQL capacity.

# Pool configuration
default_pool_size = max_connections / 4
max_db_connections = max_connections * 0.8
reserve_pool_size = default_pool_size / 5

Connection lifetime settings

server_lifetime = 3600 server_idle_timeout = 600 client_idle_timeout = 0

Next steps

Running this in production?

Want this handled for you? Running PostgreSQL replication at scale adds complexity: capacity planning, failover testing, monitoring lag spikes, and 24/7 incident response. See how we run infrastructure like this for European SaaS and fintech teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle high availability infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.