WAL archiving failures are dangerous because the database keeps working and nothing screams for attention. The first sign is usually a stale backup, or this line in the server log:

ERROR: archive command failed with exit code 1
DETAIL: archive_command="test ! -f /backups/pg_wal/%f && cp %p /backups/pg_wal/%f"

What WAL archiving actually does

After every transaction, PostgreSQL writes a WAL segment. The archive_command runs once per finished segment and should copy it to a safe place before PostgreSQL deletes it locally. If the command exits with a non-zero code the segment stays on disk, the WAL directory grows, and archiving is marked as failed.

Step 1: reproduce the command manually

Run the exact command as the PostgreSQL user. Permissions issues are the most common cause of failure.

sudo -u postgres bash -c "cp /var/lib/postgresql/16/main/pg_wal/000000010000000000000001 /backups/pg_wal/"

Step 2: check the usual suspects

  • Destination permissions - the PostgreSQL user must own the backup directory.
  • Disk space - a full target filesystem fails every copy.
  • Path quoting - spaces in paths break naive commands.
  • Command exit codes - a missing test wrapper can report failure on a successful copy.

PostgreSQL will retry every 60 seconds by default, so a single noisy command can mask days of failed archiving. Always monitor the archive status, not just the log tail.

Step 3: verify with the recovery check

pg_waldump /backups/pg_wal/000000010000000000000001 | head

If segments are present and valid, the pipeline is healthy. Add an alerting rule that watches the number of .ready files in pg_wal/archive_status.

Quick reference

SymptomLikely causeFirst check
Exit code 1, file not createdPermissions or missing directoryls -la /backups/pg_wal
Exit code 1, disk reported fullTarget filesystem fulldf -h /backups
No errors but segments piling uparchive_command disabledSHOW archive_mode;

Once fixed, trigger a switchpoint and confirm the new segment archives: SELECT pg_switch_wal();