Background processes
Archiver
With archive_mode on, the archiver process copies every completed WAL segment somewhere safe using archive_command or archive_library. A segment may only be recycled after it was archived, so a failing command makes pg_wal grow.
In plain words
WAL is written in 16 MB files called segments. Point-in-time recovery needs a copy of every segment, kept outside the server. The archiver is the small process that makes those copies.
When a segment is full, PostgreSQL drops a marker file <segment>.ready into pg_wal/archive_status/ and pokes the archiver. The archiver runs your archive_command (for example a cp, or a backup tool such as pgBackRest) for the oldest ready segment. Exit code 0 means success: the marker is renamed to .done and the checkpointer may recycle the segment later.
If the command fails, the archiver tries again, and again, forever. It never skips a segment. Meanwhile new segments keep arriving and none can be removed, so pg_wal grows until the disk is full. Switch on the failure toggle and watch it happen.
For DBAs and operators
The work loop
- Wake on its latch (set when a segment is marked
.ready), or after 60 seconds at the latest. - Scan
pg_wal/archive_status/for.readyfiles, oldest first (it remembers up to 64 candidates per directory scan instead of rescanning for each file; timeline history files go first). - For each: run
archive_commandwith%p(path relative to the data directory) and%f(file name), or call thearchive_librarymodule's callback (PostgreSQL 15+;basic_archiveis shipped as an example). - Success: rename to
.done, updatepg_stat_archiver. Failure: retry after 1 second; after 3 failures in a row give up for now and sleep (60 s or until woken by the next segment).
Real log lines from a failing command:
LOG: archive command failed with exit code 1DETAIL: The failed archive command was: test ! -f /archive/000000010000000000000012 && cp pg_wal/000000010000000000000012 /archive/000000010000000000000012WARNING: archiving write-ahead log file "000000010000000000000012" failed too many times, will try again laterHow to see it
One process, started by the postmaster when archive_mode is on (or always, which also archives on a standby). ps shows its last result in the title: postgres: archiver last was 000000010000000000000011 or postgres: archiver failed on 000000010000000000000012. backend_type = 'archiver', wait_event ArchiverMain while idle, ArchiveCommand while the command runs.
SELECT archived_count, last_archived_wal, last_archived_time,
failed_count, last_failed_wal, last_failed_time
FROM pg_stat_archiver;
-- how many segments are waiting?
SELECT count(*) FROM pg_ls_archive_statusdir()
WHERE name LIKE '%.ready';If last_failed_time is newer than last_archived_time, archiving is currently stuck.
Rules for archive_command
- Return 0 only when the file is safely stored (fsynced). Returning 0 too early loses WAL silently.
- Refuse to overwrite an existing different file (
test ! -f), but returning success for an identical copy is fine and helps after a crash between copy and rename. - It runs serially, one segment at a time: a slow command (high latency to S3, compression) becomes a backlog under heavy write load. Tools such as pgBackRest offer asynchronous archiving with parallel pushes.
- Changing
archive_commandneeds only a reload;archive_modeneeds a restart. Setting botharchive_commandandarchive_libraryis an error since PostgreSQL 16. archive_timeoutcan force a segment switch on quiet systems so completed WAL becomes archivable sooner. Each switch still produces a complete WAL segment file: 16 MB by default, or whateverwal_segment_sizewas chosen when the cluster was initialized.
Typical support case: pg_wal is full
Check pg_stat_archiver and the server log first. Fix the cause (permissions, full archive volume, expired credentials, missing directory) and the backlog drains by itself. Never delete files from pg_wal by hand. When the disk does fill up, the server stops with a PANIC such as PANIC: could not write to file "pg_wal/xlogtemp.1234": No space left on device Free space elsewhere or extend the volume, then start it again. Other WAL retainers to check: replication slots (pg_replication_slots.wal_status) and wal_keep_size.
If it dies
If the archive command is killed by a signal, the archiver exits with a FATAL error, for example FATAL: archive command was terminated by signal 9: Killed (or FATAL: archive command failed with exit code 137 when the shell reports the signal), and the postmaster starts a new one; nothing is lost because the .ready files are still there. If the archiver process itself is killed with SIGKILL, the postmaster performs a crash restart of the whole server.
For developers: inside the source
Where it lives
pgarch.c:PgArchiverMain(),pgarch_MainLoop(),pgarch_ArchiverCopyLoop(),pgarch_readyXlog().xlogarchive.c: the producer side:XLogArchiveNotify()(creates.ready, callsPgArchWakeup()),XLogArchiveCheckDone(),XLogArchiveCleanup().shell_archive.c: the built-in module behindarchive_command.archive_module.h: the archive module API.
Producer and consumer
When XLogWrite() finishes a segment it calls XLogArchiveNotifySeg(), which writes archive_status/<seg>.ready and sets the archiver's latch through PgArch->pgprocno. At checkpoint time RemoveOldXlogFiles() only removes or recycles a segment when XLogArchiveCheckDone() returns true. PgArchData in shared memory has just two fields: pgprocno and the atomic force_dir_scan (set by PgArchForceDirScan()).
Archiver call flow
pgarch_MainLoop()waits on its latch with timeoutPGARCH_AUTOWAKE_INTERVAL(60 s),WAIT_EVENT_ARCHIVER_MAIN.pgarch_ArchiverCopyLoop()repeatedly callspgarch_readyXlog(). That scans the directory into a binary heap ordered byready_file_comparator()(timeline history files first, then oldest) and keeps up toNUM_FILES_PER_DIRECTORY_SCAN(64) names, so it does not rescan for every file.pgarch_archiveXlog()sets the ps title and calls the module'sarchive_file_cb; forarchive_commandthat isshell_archive_file(), which runs the command insideWAIT_EVENT_ARCHIVE_COMMAND.- Success:
pgarch_archiveDone()renames.ready→.done,pgstat_report_archiver(xlog, false). Failure:pgstat_report_archiver(xlog, true),pg_usleep(1000000L), and afterNUM_ARCHIVE_RETRIES(3) the "failed too many times" WARNING and back to the main loop.
In shell_archive_file() the error level is wait_result_is_any_signal(rc, true) ? FATAL : LOG, which is why a command killed by a signal takes the archiver down. The postmaster then calls HandleChildCrash() only if the archiver exited with a status other than 0 or 1 (a FATAL exit is 1), and restarts it after PGARCH_RESTART_INTERVAL (10 s, see PgArchCanRestart()).
Writing an archive module
A library named in archive_library exports _PG_archive_module_init(), returning an ArchiveModuleCallbacks with startup_cb, check_configured_cb, archive_file_cb (required) and shutdown_cb; per-module state goes in ArchiveModuleState->private_data. contrib/basic_archive is the reference implementation (it fsyncs and renames atomically).
Experimenting
- TAP tests:
020_archive_status.pl(failing commands,.ready/.donehandling) and002_archiving.pl. pg_ls_archive_statusdir(),pg_stat_archiverandpstitles show progress live.
Parameters that shape this
archive_mode archive_command archive_library archive_timeout wal_level max_wal_size wal_keep_size
Related topics
Archiving and point-in-time recovery Write-ahead log (WAL) Checkpointer Checkpoints Postmaster Logger (logging collector) WAL sender WAL summarizer