PostgreSQL Streaming Replication sends WAL (Write-Ahead Log) records from a primary server to one or more standby servers in near-real-time, keeping standbys continuously synchronised with the primary. This provides high availability (automatic or manual failover when the primary fails), read scaling (queries can be distributed to hot standbys), and geographic redundancy. Unlike logical replication which copies specific tables, streaming replication copies the entire database cluster at the physical block level, making it faster and simpler for full-server HA. PostgreSQL 12+ uses a standby.signal file instead of recovery.conf for standby configuration. This guide sets up asynchronous streaming replication between a primary and a hot standby on RHEL 9.
Prerequisites
- Two RHEL 9 servers with PostgreSQL 16 installed
- Primary:
10.0.1.10| Standby:10.0.1.11 - PostgreSQL running on the primary; not yet initialised on the standby
Step 1 — Configure the Primary Server
# /var/lib/pgsql/16/data/postgresql.conf on the PRIMARY
listen_addresses = '*'
wal_level = replica # Required for streaming replication
max_wal_senders = 5 # Max simultaneous streaming connections
wal_keep_size = 128MB # Keep WAL segments for replication
hot_standby = on # Allow read queries on standby
# /var/lib/pgsql/16/data/pg_hba.conf — allow the standby to connect for replication
host replication replicator 10.0.1.11/32 scram-sha-256
# Create the replication user on the PRIMARY
sudo -u postgres psql -c "CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'ReplPass456!';"
systemctl reload postgresql-16
Step 2 — Base Backup to the Standby
# On the STANDBY — stop PostgreSQL if running, clear the data directory
systemctl stop postgresql-16
rm -rf /var/lib/pgsql/16/data/*
# Use pg_basebackup to copy the primary's data directory to the standby
sudo -u postgres pg_basebackup
-h 10.0.1.10
-U replicator
-D /var/lib/pgsql/16/data/
-P -R -Xs -W
# -R: writes postgresql.auto.conf with primary_conninfo automatically
# -Xs: streams WAL during the backup
# -P: shows progress
Step 3 — Configure and Start the Standby
# pg_basebackup -R already created standby.signal and primary_conninfo
# Verify /var/lib/pgsql/16/data/postgresql.auto.conf contains:
cat /var/lib/pgsql/16/data/postgresql.auto.conf
# Should include: primary_conninfo = 'host=10.0.1.10 user=replicator ...'
# The standby.signal file marks this as a standby
ls /var/lib/pgsql/16/data/standby.signal
systemctl start postgresql-16
Step 4 — Verify Replication is Running
# On the PRIMARY — check connected standbys
sudo -u postgres psql -c "SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;"
# On the STANDBY — verify it is in recovery mode
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"
# Should return: t (true)
Step 5 — Test Replication
# On PRIMARY: create a test table
sudo -u postgres psql -c "CREATE TABLE repl_test (id serial, ts timestamptz DEFAULT now()); INSERT INTO repl_test DEFAULT VALUES;"
# On STANDBY: verify it appeared (may take < 1 second)
sudo -u postgres psql -c "SELECT * FROM repl_test;"
Step 6 — Monitor Replication Lag
# On PRIMARY — check replication lag in bytes and time
sudo -u postgres psql -c "SELECT client_addr, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes FROM pg_stat_replication;"
Conclusion
PostgreSQL Streaming Replication on RHEL 9 provides a low-latency hot standby that continuously receives WAL changes from the primary. The standby can serve read-only queries, providing read scaling, and can be promoted to primary in seconds if the primary fails. For automatic failover, tools like Patroni or repmgr build on streaming replication to provide fully automated HA cluster management.
Next steps: How to Back Up PostgreSQL with pg_dump and pg_basebackup on RHEL 9, How to Configure PostgreSQL Remote Access and SSL on RHEL 9, and How to Configure Database Connection Pooling with PgBouncer on RHEL 9.