PostgreSQL streaming replication continuously ships Write-Ahead Log (WAL) records from a primary server to one or more standby replicas in near real time. The standby can serve as a warm failover target or, in hot-standby mode, accept read-only queries to offload reporting traffic from the primary. This tutorial configures a two-node streaming replication setup on RHEL 8 using PostgreSQL 14, with one primary and one replica on the same private network.
Prerequisites
- Two RHEL 8 servers: primary (e.g. 192.168.1.10) and replica (e.g. 192.168.1.20)
- PostgreSQL 14 installed and initialised on both servers
- The primary PostgreSQL instance running with data already in place (or freshly initialised)
- Network connectivity between the two hosts on port 5432
- sudo access on both servers
Step 1 — Configure the Primary: postgresql.conf
Replication requires the WAL level to be set to replica (or higher) so that sufficient WAL data is written for standby servers to apply. Set max_wal_senders to the number of standbys you plan to connect, and set wal_keep_size to retain enough WAL segments on disk in case a replica falls behind temporarily.
sudo nano /var/lib/pgsql/data/postgresql.conf
Set or uncomment the following directives on the primary:
wal_level = replica
max_wal_senders = 3
wal_keep_size = 256 # MB; retain 256 MB of WAL segments
hot_standby = on # allow read queries on the standby
listen_addresses = '*'
Step 2 — Create the Replication Role on the Primary
PostgreSQL requires a dedicated role with the REPLICATION attribute for standby connections. Create the role with a strong password and avoid granting it any other privileges.
sudo -u postgres psql <<'SQL'
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'R3pl!catorPass#1';
SQL
Step 3 — Update pg_hba.conf on the Primary
Add a host replication entry that allows the replicator role to connect from the replica’s IP address. Use scram-sha-256 for modern PostgreSQL versions; fall back to md5 only if the client library does not support SCRAM.
sudo nano /var/lib/pgsql/data/pg_hba.conf
Append the following line, replacing the IP with your replica’s address:
host replication replicator 192.168.1.20/32 scram-sha-256
sudo systemctl reload postgresql
Also open port 5432 through firewalld on the primary if not already open:
sudo firewall-cmd --permanent --add-port=5432/tcp
sudo firewall-cmd --reload
Step 4 — Take a Base Backup on the Replica
Stop the PostgreSQL service on the replica, clear its data directory, and use pg_basebackup to stream a consistent copy of the primary’s data directory over the replication connection. The -R flag automatically writes the primary_conninfo settings into postgresql.auto.conf and creates the standby.signal file that marks the server as a standby.
# Run on the REPLICA server
sudo systemctl stop postgresql
# Clear the existing (empty) data directory
sudo rm -rf /var/lib/pgsql/data/*
sudo -u postgres pg_basebackup
-h 192.168.1.10
-U replicator
-D /var/lib/pgsql/data
-P
-Xs
-R
The -Xs flag streams WAL during the backup (streaming mode), -P shows progress, and -R writes the recovery configuration automatically.
Step 5 — Verify postgresql.auto.conf on the Replica
After pg_basebackup completes, inspect the auto-configuration file to confirm that primary_conninfo and the standby signal are correct before starting the replica.
cat /var/lib/pgsql/data/postgresql.auto.conf
# Expected output includes:
# primary_conninfo = 'user=replicator host=192.168.1.10 port=5432 ...'
ls /var/lib/pgsql/data/standby.signal
# File must exist for PostgreSQL to start in standby mode
Step 6 — Start the Standby and Verify Replication
Start PostgreSQL on the replica. It will enter continuous recovery mode, connect to the primary, and begin applying WAL. Confirm replication is active by querying the pg_stat_replication view on the primary.
# On the REPLICA
sudo systemctl start postgresql
# On the PRIMARY — verify the standby has connected
sudo -u postgres psql -c "SELECT application_name, state, sent_lsn, replay_lsn
FROM pg_stat_replication;"
A row with state = streaming confirms that the standby is receiving WAL records in real time. The difference between sent_lsn and replay_lsn indicates replication lag; under normal load this should be negligible.
Conclusion
You have configured PostgreSQL streaming replication on RHEL 8 by enabling WAL replication on the primary, creating a dedicated replication role, authorising the replica in pg_hba.conf, cloning the primary with pg_basebackup -R, and verifying the connection through pg_stat_replication. The standby is now applying WAL in real time and can serve read-only queries in hot-standby mode or be promoted to primary if the original fails.
Next steps: How to Promote a PostgreSQL Standby to Primary on RHEL 8, How to Back Up PostgreSQL with pg_dump and pg_basebackup on RHEL 8, and How to Configure PostgreSQL Remote Access and SSL on RHEL 8.