Home / Alt manpages / pg_receivewal(1)

  • pg_receivewal(1)
  • User command
  • linux

Archive PostgreSQL WAL Safely with pg_receivewal

You will finish with a controlled way to stream 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 also 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 is postgresql-client-16, version 16.15-0ubuntu0.24.04.1 on this machine. 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

Confirm that 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, and it must not be a PostgreSQL data directory or a directory already containing unrelated WAL files. If you are testing as your own user, 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 that 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:

  • The connection is allowed by pg_hba.conf as a replication connection.
  • The login has REPLICATION privilege or is a superuser.
  • max_wal_senders leaves a session available for this receiver.
  • 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 an idempotent setup command, but it does not prove that 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

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 putting a password in 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. It writes 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 are more useful 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 provide timely feedback. Do not accidentally put it in a configuration where synchronous_commit=remote_apply waits for this receiver: 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 after the receiver reaches the specified LSN. The LSN must be supplied by 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

If a connection error causes an unwanted retry loop, rerun the diagnostic command with --no-loop. A password prompt is expected when the server requests password authentication; use --no-password in a non-interactive check if prompting would hang a job. A missing directory, unwritable destination, invalid slot or rejected replication connection 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

  • The installed PostgreSQL client version and output directory are confirmed.
  • A replication user, pg_hba.conf rule, WAL sender capacity and physical slot are verified by the administrator.
  • The receiver writes to a dedicated directory and reconnect behaviour is understood.
  • Flush behaviour matches the backup and synchronous-standby design; --no-sync is not used for production archiving.
  • Disk usage is monitored on the archive host and the primary server.
  • A bounded test or clean shutdown returned status 0, and the received files are included in a tested restore process.