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 a recovery.conf file 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 WAL
  • primary_conninfo — connection string for the streaming replication connection to the primary
  • trigger_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.