How to Set Up PostgreSQL Streaming Replication on RHEL 7
PostgreSQL streaming replication allows a standby server to continuously receive and apply WAL (Write-Ahead Log) records from a primary server in near real time, maintaining an up-to-date hot standby copy of the database. This is the foundation of high availability and read scaling strategies in PostgreSQL environments. On RHEL 7, the setup involves configuring the primary server to broadcast WAL changes, creating a dedicated replication user, seeding the standby with a base backup, and configuring the standby to apply the stream. This guide walks through the complete process for PostgreSQL 9.6, including how to promote a standby to primary if needed.
Prerequisites
- Two RHEL 7 servers: one designated primary (e.g.,
10.0.0.10) and one standby (e.g.,10.0.0.11) - PostgreSQL 9.6 installed on both servers via the PGDG repository
- Both servers initialized with
postgresql-9.6-setup initdb - Primary PostgreSQL service running; standby service stopped for the initial base backup
- Network connectivity between the two servers on port 5432
- Root or sudo access on both servers
Step 1: Configure the Primary Server — postgresql.conf
On the primary server, edit postgresql.conf to enable WAL streaming:
sudo vi /var/lib/pgsql/9.6/data/postgresql.conf
Add or update the following parameters:
# Enable WAL archiving level required for replication
wal_level = replica
# Number of concurrent replication connections allowed
max_wal_senders = 5
# Keep this many WAL segment files in pg_xlog to allow standby to catch up
# after a network interruption (each segment is 16 MB by default)
wal_keep_segments = 64
# Allow connections from the standby's IP
listen_addresses = '*'
# Optional: enable replication slots for guaranteed WAL retention
# max_replication_slots = 5
In PostgreSQL 9.6, wal_level = replica replaces the older hot_standby value and enables both streaming replication and read queries on the standby. The wal_keep_segments value of 64 reserves approximately 1 GB of WAL files on the primary, giving the standby a buffer to catch up after brief disconnections.
Step 2: Create a Replication User on the Primary
Create a dedicated PostgreSQL role with the REPLICATION privilege. Never use the superuser postgres account for replication connections:
sudo -u postgres psql
CREATE ROLE replicator WITH REPLICATION LOGIN ENCRYPTED PASSWORD 'StrongRepl1cati0n!';
q
Verify the role was created:
sudo -u postgres psql -c "du replicator"
Step 3: Update pg_hba.conf on the Primary
Allow the standby server to connect using the replication protocol. Edit pg_hba.conf on the primary:
sudo vi /var/lib/pgsql/9.6/data/pg_hba.conf
Add the following line, substituting the standby server’s IP address:
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 10.0.0.11/32 md5
The keyword replication in the DATABASE column is a special value in PostgreSQL that grants access for WAL streaming — it is not an actual database name.
Restart the primary to apply all configuration changes:
sudo systemctl restart postgresql-9.6
Open the firewall on the primary to accept connections from the standby:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.11/32" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload
Step 4: Take a Base Backup on the Standby
On the standby server, stop PostgreSQL and clear the existing data directory, then use pg_basebackup to clone the primary:
# Stop PostgreSQL on the standby (it should not be running during base backup)
sudo systemctl stop postgresql-9.6
# Clear the standby data directory
sudo rm -rf /var/lib/pgsql/9.6/data/*
# Run pg_basebackup as the postgres user
sudo -u postgres pg_basebackup
-h 10.0.0.10
-U replicator
-D /var/lib/pgsql/9.6/data
-P
-R
--xlog-method=stream
Parameter explanation:
-h 10.0.0.10— connect to the primary server-U replicator— use the replication role created earlier-D /var/lib/pgsql/9.6/data— write the backup to the standby’s data directory-P— show progress during the backup-R— automatically write arecovery.conffile in the data directory--xlog-method=stream— stream WAL concurrently during the backup to ensure consistency
You will be prompted for the replicator password. Once complete, verify the data directory is populated:
ls /var/lib/pgsql/9.6/data/
Step 5: Configure recovery.conf on the Standby
The -R flag in the previous step auto-creates recovery.conf, but review and complete it:
sudo vi /var/lib/pgsql/9.6/data/recovery.conf
Ensure it contains:
standby_mode = 'on'
primary_conninfo = 'host=10.0.0.10 port=5432 user=replicator password=StrongRepl1cati0n! application_name=standby1'
trigger_file = '/tmp/postgresql.trigger.5432'
recovery_target_timeline = 'latest'
standby_mode = on— keeps the server in standby mode after catching up, waiting for more WALprimary_conninfo— connection string for the streaming replication connection to the primarytrigger_file— if this file exists on disk, the standby will promote itself to primary (used for manual failover)recovery_target_timeline = 'latest'— follow the latest timeline, which is important after a failover
Step 6: Enable Hot Standby Queries (Optional)
To allow read-only queries on the standby while it is in recovery, add to postgresql.conf on the standby:
hot_standby = on
This allows your application to send SELECT queries to the standby for read scaling, while all write operations continue on the primary.
Step 7: Start the Standby
sudo systemctl start postgresql-9.6
sudo systemctl status postgresql-9.6
Check the standby logs to confirm it is receiving WAL from the primary:
sudo tail -50 /var/lib/pgsql/9.6/data/pg_log/postgresql-*.log
You should see lines such as:
LOG: started streaming WAL from primary at 0/3000000 on timeline 1
On the primary, verify the standby is listed as a connected sender:
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"
The output should show the standby’s application name, client address, replication state (streaming), and the WAL send/write/flush/replay positions.
Step 8: Promote the Standby to Primary
If the primary server fails and you need to promote the standby, there are two methods:
Method 1: Trigger File
# On the standby server, create the trigger file defined in recovery.conf
touch /tmp/postgresql.trigger.5432
PostgreSQL detects the trigger file, finishes applying any remaining WAL, and transitions to normal read-write mode. The recovery.conf file is renamed to recovery.done.
Method 2: pg_ctl promote
sudo -u postgres /usr/pgsql-9.6/bin/pg_ctl promote
-D /var/lib/pgsql/9.6/data
After promotion, confirm the former standby is now accepting writes:
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"
The result should be f (false), meaning the server is no longer in recovery mode and is now a primary.
Conclusion
PostgreSQL streaming replication on RHEL 7 provides a robust and low-latency mechanism for keeping a standby server synchronized with a primary. The combination of wal_level = replica, max_wal_senders, wal_keep_segments, a dedicated replication role, and pg_basebackup makes the initial setup straightforward. With hot_standby = on, the standby can simultaneously serve read queries and absorb production write traffic through replication — an excellent strategy for both high availability and horizontal read scaling. Test your promotion procedure regularly so that failover under pressure proceeds without surprises.