Home / Alt manpages / pg_receivewal(1)

  • pg_receivewal(1)
  • User command
  • linux

Archive PostgreSQL WAL Safely with pg_receivewal

A replication slot without a live receiver behind it is a disk-filling time bomb, and pg_receivewal is the tool that keeps the promise the slot makes. This guide sets up a controlled stream of PostgreSQL write-ahead log (WAL) files into a local archive directory, using an existing physical replication slot and a receiver that reports durable progress. The examples match the PostgreSQL 16.15 client installed here.

Allow about 20 minutes for the client-side checks, plus whatever time your database administrator needs to approve the replication user, pg_hba.conf rule and slot.

  • You need: PostgreSQL client utilities, a reachable PostgreSQL server, a user with the REPLICATION privilege or superuser access, and enough free space for the archive.
  • The server needs: at least one available WAL sender.

Warning

This is a backup component, not a complete point-in-time recovery plan. Test restoring from the received files before relying on them. A replication slot protects WAL from being recycled, but a receiver that stops can eventually fill the primary server's disk.

1. Confirm the installed client

Start with read-only checks. They do not need elevated privileges:

$ pg_receivewal --version
pg_receivewal (PostgreSQL) 16.15
$ command -v pg_receivewal
/usr/bin/pg_receivewal

The installed package here is postgresql-client-16, version 16.15-0ubuntu0.24.04.1. Your package revision and binary path may differ. Keep the full option names in scripts so a future reader can see what each value controls.

Checkpoint

pg_receivewal --help shows --directory, --slot, --synchronous and --no-loop. The output directory is mandatory.

2. Prepare an archive directory

Choose a directory on storage with a suitable retention and monitoring policy. This example creates a new directory below /var/backups, so it normally requires elevated privileges:

$ sudo install -d -o postgres -g postgres -m 0750 /var/backups/postgresql/wal
$ sudo -u postgres test -w /var/backups/postgresql/wal && echo writable
writable
  • Use the account that will run the receiver. The directory must be writable by that account.
  • Do not reuse a data directory. It must not be a PostgreSQL data directory or a directory already containing unrelated WAL files.
  • Testing as yourself? Use a directory under your home directory instead and omit sudo.

This step changes filesystem state. To undo the empty test directory, first confirm its contents are disposable, then remove only that exact directory with sudo rmdir /var/backups/postgresql/wal. Do not remove an archive directory containing WAL you have not backed up elsewhere.

3. Check the server-side prerequisites

Ask the database administrator to verify these items before starting a long-running receiver:

  • Connection allowed. pg_hba.conf permits this as a replication connection.
  • Privilege granted. The login has REPLICATION privilege or is a superuser.
  • Sender capacity free. max_wal_senders leaves a session available for this receiver.
  • Slot ready. The named physical replication slot already exists, or its creation has been approved.

Use a distinct slot name such as backup_wal_01. Creating a slot retains WAL on the server and therefore changes server state. If you must create one, review the retention risk first:

$ pg_receivewal --dbname='host=DB_HOST port=5432 user=wal_receiver' \
    --slot=backup_wal_01 --create-slot

A successful create-slot action exits without starting the stream. If the slot already exists, the command fails unless you add --if-not-exists. That option makes setup idempotent, but it does not prove the existing slot has the name, type or ownership you intended.

To undo a slot you created, stop every receiver using it and ask the administrator to check that no backup still depends on it, then run:

$ pg_receivewal --dbname='host=DB_HOST port=5432 user=wal_receiver' \
    --slot=backup_wal_01 --drop-slot

Warning

Dropping a slot can allow the server to remove WAL that was being retained for this receiver. Treat it as irreversible for the backup chain.

4. Start a normal WAL stream

Use the existing slot and a password method approved for your environment. A .pgpass entry is usually safer for an unattended service than a password on the command line. The database name in --dbname is ignored by this utility, but connection parameters in a connection string override conflicting command-line values.

$ pg_receivewal \
    --host=DB_HOST \
    --port=5432 \
    --username=wal_receiver \
    --directory=/var/backups/postgresql/wal \
    --slot=backup_wal_01 \
    --verbose

The process runs continuously, writing WAL as the server generates it, and normally reconnects indefinitely after a non-fatal connection failure. Stop it with Control+C or SIGTERM; either signal produces normal exit status 0. Do not use --no-loop unless your supervisor should handle retries itself.

In a second terminal, check that files are appearing without changing them:

$ find /var/backups/postgresql/wal -maxdepth 1 -type f -printf '%f %s bytes\n' | sort | tail
$ df -h /var/backups/postgresql/wal

WAL generation is workload-dependent, so an empty directory immediately after startup is not proof of failure. Verbose output and the receiver's exit status matter more than waiting for a particular filename.

5. Choose the flush and feedback behaviour deliberately

By default, pg_receivewal flushes received WAL when a WAL file closes. Add --synchronous when you need each received write flushed promptly and an immediate status packet sent after the flush:

$ pg_receivewal \
    --host=DB_HOST --port=5432 --username=wal_receiver \
    --directory=/var/backups/postgresql/wal \
    --slot=backup_wal_01 --synchronous --verbose

This option is also required if the receiver is deliberately configured as a synchronous standby and must give timely feedback.

Warning

Do not accidentally put this receiver in a configuration where synchronous_commit=remote_apply waits for it. pg_receivewal stores WAL but does not apply it, so commits can block behind a receiver that never catches up. Use a non-matching application name or an appropriate synchronous-standby configuration.

Avoid --no-sync for production archiving. It can be faster, but an operating system crash can leave received WAL corrupt, and the option cannot be combined with --synchronous.

6. Test a bounded run and common failures

For a supervised smoke test, --endpos stops normally once the receiver reaches the specified LSN. The LSN must come from your PostgreSQL backup procedure; do not invent one:

$ pg_receivewal --host=DB_HOST --port=5432 --username=wal_receiver \
    --directory=/var/backups/postgresql/wal --slot=backup_wal_01 \
    --endpos=START_LSN --verbose
$ printf 'exit status: %s\n' "$?"
exit status: 0
  • Connection error causing a retry loop? Rerun the diagnostic command with --no-loop.
  • Unexpected password prompt? Expected when the server requests password authentication; use --no-password in a non-interactive check if prompting would hang a job.
  • Missing directory, unwritable destination, invalid slot or rejected connection? That is a configuration error, not a reason to add sudo blindly.

Check disk usage on both sides. The archive destination can fill locally, while a stalled slot can fill the primary server. Set alerts before turning this into a service. If the receiver must be replaced, stop the old process cleanly, confirm the new process uses the same slot and directory, and keep the old files in place.

Done means

  • Client confirmed. The installed PostgreSQL client version and output directory are known.
  • Server side checked. A replication user, pg_hba.conf rule, WAL sender capacity and physical slot are verified by the administrator.
  • Receiver running correctly. It writes to a dedicated directory and its reconnect behaviour is understood.
  • Durability decided. Flush behaviour matches the backup and synchronous-standby design; --no-sync is not used for production archiving.
  • Disk monitored. Usage is watched on both the archive host and the primary server.
  • Restore tested. A bounded test or clean shutdown returned status 0, and the received files are part of a tested restore process.