The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Inspect sender capacity | SHOW wal_level; SHOW max_wal_senders; SHOW max_replication_slots; | View examples |
| Create replication login | CREATE ROLE standby_rep LOGIN REPLICATION; \password standby_rep | View examples |
| Create physical slot | SELECT *
FROM pg_create_physical_replication_slot('standby_a'); | View examples |
| Seed a standby | pg_basebackup -h primary.example -U standby_rep -D /pgdata/18 -R -X stream -S standby_a -C --progress | View examples |
| Configure upstream | primary_conninfo = 'host=primary.example user=standby_rep application_name=standby_a sslmode=verify-full' | View examples |
| Use permanent slot | primary_slot_name = 'standby_a' | View examples |
| Inspect WAL senders | SELECT application_name,
state,
sync_state,
sent_lsn,
flush_lsn,
replay_lsn
FROM pg_stat_replication; | View examples |
| Measure replay byte lag | SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication; | View examples |
| Check standby replay | SELECT pg_is_in_recovery(),
pg_last_wal_receive_lsn(),
pg_last_wal_replay_lsn(),
pg_last_xact_replay_timestamp(); | View examples |
| Require priority standbys | synchronous_standby_names = 'FIRST 1 (standby_a, standby_b)' | View examples |
| Require standby quorum | synchronous_standby_names = 'ANY 2 (standby_a, standby_b, standby_c)' | View examples |
| Wait for replay | SET LOCAL synchronous_commit = 'remote_apply'; | View examples |
| Inspect recovery conflicts | SELECT datname,
confl_snapshot,
confl_lock,
confl_bufferpin,
confl_deadlock
FROM pg_stat_database_conflicts; | View examples |
| Verify server role | SELECT pg_is_in_recovery(); | View examples |
| Promote an approved standby | SELECT pg_promote(wait => true, wait_seconds => 60); | View examples |
| Preview pg_rewind | pg_rewind --target-pgdata=/pgdata/18 --source-server='host=new-primary dbname=postgres user=rewind' --dry-run --progress | View examples |
Physical streaming replication continuously replays WAL into a cluster-level standby and is the normal PostgreSQL foundation for low-recovery-time failover and read replicas. It is asynchronous by default, does not replace backups, and does not include a failure detector or cluster manager. Build it as a complete system: authenticated transport, a recoverable base backup, bounded WAL retention, measurable durability and replay targets, client routing, split-brain fencing, and rehearsed standby rebuilds after promotion.
Step by step
Detailed examples
Prepare a compatible, authenticated physical topology
Physical replication transfers WAL records for the entire cluster, so a standby must use the same major PostgreSQL version, compatible platform and architecture, and matching tablespace paths. wal_level=replica or higher, max_wal_senders, and optionally max_replication_slots are set on every node that might later become a sender. Use a dedicated LOGIN REPLICATION role, a source-specific pg_hba.conf rule, verified TLS where the network is not trusted, and a protected password file. Size CPU, I/O, network, WAL generation, and archive capacity before counting a standby toward recovery objectives.
SHOW server_version;
SHOW wal_level;
SHOW max_wal_senders;
SHOW max_replication_slots;
SHOW archive_mode;
CREATE ROLE standby_rep LOGIN REPLICATION;
\password standby_rep Note: Add a narrow pg_hba.conf rule such as a hostssl replication entry for the standby address and SCRAM authentication. Never store the real password in this SQL.
Seed each standby from a recoverable base backup
A standby needs a consistent physical base backup plus every WAL segment required from that backup's checkpoint onward. pg_basebackup -R writes standby.signal and connection settings; -X stream includes required WAL while the backup runs. The target directory must be empty and owned correctly, tablespaces need deliberate mapping, and the source must retain WAL until the receiver catches up. Take the standby offline from client traffic until recovery state, checksums, extensions, configuration, and replay are verified.
pg_basebackup \
--host=primary.example \
--username=standby_rep \
--pgdata=/pgdata/18 \
--write-recovery-conf \
--wal-method=stream \
--create-slot --slot=standby_a \
--checkpoint=fast --progress --verbose Note: Run only against an approved source and an empty target. --checkpoint=fast can increase primary I/O. Use PostgreSQL 18 client tools for a PostgreSQL 18 cluster and supply credentials outside the command line.
Bound WAL retention without sacrificing recoverability
Streaming alone does not guarantee a disconnected standby can catch up. wal_keep_size retains only a minimum, a physical slot retains WAL according to consumer progress, and a continuous WAL archive can bridge outages and supports PITR. Slots are operational leases: an inactive consumer can fill pg_wal when max_slot_wal_keep_size is unlimited, while a finite cap can invalidate a lagging slot and require a rebuild. Use one stable slot per standby, alert on retained bytes and disk headroom, and drop slots only after proving the consumer is permanently retired.
SELECT slot_name, active, restart_lsn, wal_status, safe_wal_size, invalidation_reason,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots
WHERE slot_type = 'physical'
ORDER BY slot_name;
SHOW wal_keep_size;
SHOW max_slot_wal_keep_size; Note: A null or low safe_wal_size requires investigation. Do not drop or advance a slot as a disk-space shortcut unless the standby is intentionally being rebuilt.
Measure transport, flush, replay, and data freshness separately
On the primary, pg_stat_replication reports one row per connected WAL sender with sent, write, flush, and replay positions. Byte lag derived with pg_wal_lsn_diff is capacity-oriented; write_lag, flush_lag, and replay_lag estimate recent synchronous-commit delay and can remain nonzero briefly or become null when idle, so they are not a countdown to recovery. On the standby, inspect the WAL receiver, recovery state, receive/replay LSNs, and last replayed transaction timestamp. Alert on missing connections, state changes, slot retention, replay pauses, archive failures, disk space, and an application-level freshness sentinel.
SELECT application_name, client_addr, state, sync_state,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) AS send_gap,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), flush_lsn)) AS flush_gap,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_gap,
write_lag, flush_lag, replay_lag, reply_time
FROM pg_stat_replication
ORDER BY application_name; SELECT pg_is_in_recovery() AS is_standby,
pg_last_wal_receive_lsn() AS received,
pg_last_wal_replay_lsn() AS replayed,
pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()) AS replay_queue_bytes,
pg_last_xact_replay_timestamp() AS last_replayed_commit;
SELECT status, sender_host, sender_port, slot_name, written_lsn, flushed_lsn
FROM pg_stat_wal_receiver; Define exactly what synchronous commits promise
Streaming replication is asynchronous by default, so acknowledged primary commits can be absent after failover by roughly the outstanding replication delay. synchronous_standby_names selects priority (FIRST) or quorum (ANY) candidates, while synchronous_commit chooses whether a transaction waits for remote receipt, durable flush, or replay. Synchronous mode adds at least network and standby latency and can block writes when insufficient candidates remain. Choose a setting from a documented RPO and failure model, test degraded behavior, and remember that a transaction can locally reduce synchronous_commit unless privileges and procedures prevent it.
SHOW synchronous_standby_names;
SHOW synchronous_commit;
SELECT application_name, state, sync_state, sync_priority,
write_lag, flush_lag, replay_lag
FROM pg_stat_replication
ORDER BY sync_priority, application_name; Note: A configured name is not proof that the required standby is healthy. Confirm sync_state and rehearse the latency and availability behavior when candidates disappear.
Balance read replicas against unapplied WAL
Hot standby queries use a read-only view while WAL replay changes the same files and rows underneath them. Vacuum cleanup, locks, buffer pins, snapshots, tablespace changes, and deadlocks can create recovery conflicts. PostgreSQL may delay replay up to max_standby_streaming_delay and then cancel queries; allowing unlimited delay increases staleness and retention upstream. hot_standby_feedback can reduce cleanup conflicts but can cause table bloat on the primary. Set an explicit product priority between fresh recovery and long analytics, route workloads accordingly, and monitor cancellations, replay queue growth, and primary bloat together.
SHOW hot_standby;
SHOW hot_standby_feedback;
SHOW max_standby_streaming_delay;
SELECT datname, confl_tablespace, confl_lock, confl_snapshot,
confl_bufferpin, confl_deadlock, confl_active_logicalslot
FROM pg_stat_database_conflicts
ORDER BY datname; Note: Conflict counters are cumulative. Track rates and correlate them with replay delay, canceled-query logs, and dead-tuple growth on the primary.
Fence the old primary before promotion and client redirection
PostgreSQL can promote a standby, but it does not decide that a primary has failed, obtain quorum, fence the old writer, move an address, or rewrite application connection targets. An HA controller or human runbook must distinguish a server failure from a network partition. Before promotion, stop or fence the old primary, choose the most advanced eligible standby, understand any asynchronous data-loss window, and preserve evidence. After promotion, verify the new timeline and writeability before redirecting clients, then prevent the former primary from rejoining as a writer.
-- Candidate, before promotion: must return true.
SELECT pg_is_in_recovery();
SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();
-- Only after the old primary is fenced and the candidate is selected.
SELECT pg_promote(wait => true, wait_seconds => 60);
-- After promotion: must return false.
SELECT pg_is_in_recovery(); Note: These SQL functions are not a failover system. Promotion is a state-changing operation; never run it merely because a monitoring check timed out.
Rewind or rebuild every former primary before rejoining
Promotion creates a new timeline. A former primary may contain changes absent from the winner and must never simply restart into the topology. pg_rewind can copy changed blocks and configuration from the new primary when prerequisites such as data checksums or wal_log_hints and required WAL are satisfied; otherwise take a fresh base backup. Stop the target cleanly, preserve needed configuration and evidence, run a dry-run where supported, verify recovery settings, and reconnect it as a standby. Keep redundant protection restored before declaring the incident closed.
pg_rewind \
--target-pgdata=/pgdata/18 \
--source-server='host=new-primary.example dbname=postgres user=rewind sslmode=verify-full' \
--dry-run --progress Note: The target must be stopped and the source connection appropriately privileged. A successful dry-run is not permission to proceed; back up configuration and follow the exact PostgreSQL 18 pg_rewind runbook.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
- PostgreSQL Global Development GroupPostgreSQL: Log-Shipping Standby Serverspostgresql.org
- PostgreSQL Global Development GroupPostgreSQL: Failoverpostgresql.org
- PostgreSQL Global Development GroupPostgreSQL: Replication Configurationpostgresql.org
- PostgreSQL Global Development GroupPostgreSQL: Monitoring Statisticspostgresql.org
- PostgreSQL Global Development GroupPostgreSQL: Hot Standbypostgresql.org
- PostgreSQL Global Development GroupPostgreSQL: Replication Slotspostgresql.org
- PostgreSQL Global Development GroupPostgreSQL: pg_basebackuppostgresql.org
- PostgreSQL Global Development GroupPostgreSQL: pg_rewindpostgresql.org
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



