The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Inspect sender capacitySHOW wal_level; SHOW max_wal_senders; SHOW max_replication_slots;View examples
Create replication loginCREATE ROLE standby_rep LOGIN REPLICATION; \password standby_repView examples
Create physical slotSELECT * FROM pg_create_physical_replication_slot('standby_a');View examples
Seed a standbypg_basebackup -h primary.example -U standby_rep -D /pgdata/18 -R -X stream -S standby_a -C --progressView examples
Configure upstreamprimary_conninfo = 'host=primary.example user=standby_rep application_name=standby_a sslmode=verify-full'View examples
Use permanent slotprimary_slot_name = 'standby_a'View examples
Inspect WAL sendersSELECT application_name, state, sync_state, sent_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;View examples
Measure replay byte lagSELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes FROM pg_stat_replication;View examples
Check standby replaySELECT pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(), pg_last_xact_replay_timestamp();View examples
Require priority standbyssynchronous_standby_names = 'FIRST 1 (standby_a, standby_b)'View examples
Require standby quorumsynchronous_standby_names = 'ANY 2 (standby_a, standby_b, standby_c)'View examples
Wait for replaySET LOCAL synchronous_commit = 'remote_apply';View examples
Inspect recovery conflictsSELECT datname, confl_snapshot, confl_lock, confl_bufferpin, confl_deadlock FROM pg_stat_database_conflicts;View examples
Verify server roleSELECT pg_is_in_recovery();View examples
Promote an approved standbySELECT pg_promote(wait => true, wait_seconds => 60);View examples
Preview pg_rewindpg_rewind --target-pgdata=/pgdata/18 --source-server='host=new-primary dbname=postgres user=rewind' --dry-run --progressView 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

01

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.

Review primary capability and create a narrow login
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.

Back to quick reference ↑
02

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.

Representative PostgreSQL 18 standby seed
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.

Back to quick reference ↑
03

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.

Audit physical slot retention
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.

Back to quick reference ↑
04

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.

Primary-side replication health
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;
Standby-side receive and replay health
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;
Back to quick reference ↑
05

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.

Inspect effective synchronous topology
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.

Back to quick reference ↑
06

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.

Inspect conflict and replay policy
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.

Back to quick reference ↑
07

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.

Role checks around an externally approved promotion
-- 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.

Back to quick reference ↑
08

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.

Assess rewind before changing the stopped target
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.

Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. PostgreSQL Global Development GroupPostgreSQL: Log-Shipping Standby Serverspostgresql.org
  2. PostgreSQL Global Development GroupPostgreSQL: Failoverpostgresql.org
  3. PostgreSQL Global Development GroupPostgreSQL: Replication Configurationpostgresql.org
  4. PostgreSQL Global Development GroupPostgreSQL: Monitoring Statisticspostgresql.org
  5. PostgreSQL Global Development GroupPostgreSQL: Hot Standbypostgresql.org
  6. PostgreSQL Global Development GroupPostgreSQL: Replication Slotspostgresql.org
  7. PostgreSQL Global Development GroupPostgreSQL: pg_basebackuppostgresql.org
  8. 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.

Share feedback