PostgreSQL 17 Native Incremental Backup — The Reality of pg_basebackup Chain Operations and Recovery
Up through PostgreSQL 16, pg_basebackup always produced only full backups. If copying hundreds of gigabytes of database every night felt impractical, your only option was to attach an external tool like pgBackRest, Barman, or WAL-G. I remember the first time I took over a production PostgreSQL instance, I found myself puzzled — "why doesn't the built-in tooling support incremental backups?" — and ended up digging through pgBackRest documentation.
PostgreSQL 17 GA changed that. The --incremental flag was added to pg_basebackup, and pg_combinebackup arrived alongside it to merge chains. Native incremental backups are now possible within PostgreSQL itself, with no external tools required.
This article evaluates, as of 2026, whether this feature is ready for production use — covering everything from WAL Summarizer overhead to the pitfalls of chain management from a practical standpoint. The short answer is: "it's usable, but with conditions."
How It Works — WAL Summarizer and backup_manifest
WAL Summarizer
Incremental backup comes down to one core question: "how do we know which blocks changed since the last backup?" PostgreSQL 17 solves this with a background process called the WAL Summarizer.
When summarize_wal = on is set, PostgreSQL analyzes the WAL log and records the list of changed data blocks under $PGDATA/pg_wal/summaries/. These summary files are what allow pg_basebackup --incremental to determine "which blocks need to be fetched."
-- summarize_wal is a SIGHUP-context parameter, so it takes effect on reload in principle.
-- However, always verify the Summarizer process state depending on your environment.
ALTER SYSTEM SET summarize_wal = 'on';
SELECT pg_reload_conf();
-- Check Summarizer state (verify that summarized_lsn is progressing)
SELECT * FROM pg_get_wal_summarizer_state();
-- List generated WAL summary files
SELECT * FROM pg_available_wal_summaries();If summarized_lsn from pg_get_wal_summarizer_state() is not keeping up with the current WAL position, incremental backups may fail or produce a larger-than-expected chain — so it is worth adding this to your monitoring.
backup_manifest
Each backup directory contains a backup_manifest file holding file paths, sizes, modification times, and checksums (CRC32C or SHA variants). When running an incremental backup, passing this file to the --incremental option causes only the blocks that changed after that point to be fetched selectively. The checksums allow file corruption within the chain to be detected at merge time.
Building a Backup Chain
Format Choice — plain Recommended
pg_combinebackup requires plain-format data directories as input. Backups made with -F tar must be extracted separately before merging, so if you plan to run incremental chains continuously, -F plain (the default) is the practical choice.
# 1. Full backup (starting point of the chain)
# Note: --checkpoint=fast forces an immediate checkpoint, causing an I/O spike.
# In production, leave it at spread (the default), or use fast only during low-load windows.
pg_basebackup \
-D /backup/full \
-F plain \
-P
# 2. First incremental backup (references the full backup's manifest)
pg_basebackup \
-D /backup/inc1 \
--incremental=/backup/full/backup_manifest \
-F plain \
-P
# 3. Second incremental backup (references inc1's manifest)
pg_basebackup \
-D /backup/inc2 \
--incremental=/backup/inc1/backup_manifest \
-F plain \
-PEach incremental backup must reference the backup_manifest of the immediately preceding backup (full or incremental). The chain order must not be broken.
Recovery: Merging with pg_combinebackup
Incremental backup files cannot be used for recovery on their own. You must use pg_combinebackup to synthesize the full backup plus all incrementals into a single data directory before PostgreSQL can start.
# Merge a full backup + 2 incrementals into a single data directory
pg_combinebackup \
/backup/full \
/backup/inc1 \
/backup/inc2 \
-o /restore/combinedThe merged directory is a complete data directory identical in structure to one created by initdb. The subsequent steps differ depending on your goal.
- Recover to crash point (replay the most recent WAL): You can start as-is without any signal file, but typically you configure
restore_commandto fetch WAL from the archive in order. - PITR (point-in-time recovery): Set
restore_commandandrecovery_target_time(orrecovery_target_lsn, etc.) inpostgresql.conforpostgresql.auto.conf, create an emptyrecovery.signalfile in the data directory, then start. (Note:recovery.confwas removed in PostgreSQL 12.) - Attach as a standby: Set
primary_conninfo, create astandby.signalfile, then start.
# PITR example — after moving the merged directory to DATA
cat >> $PGDATA/postgresql.auto.conf <<EOF
restore_command = 'cp /wal_archive/%f %p'
recovery_target_time = '2026-08-27 14:30:00+09'
recovery_target_action = 'promote'
EOF
touch $PGDATA/recovery.signal
pg_ctl -D $PGDATA startWhat incremental backup saves you is backup time and transfer volume — not recovery itself. In fact, the recovery path is longer than restoring from a single full backup, because of the added pg_combinebackup merge step. Actual recovery time depends heavily on chain length, total volume of data being merged, and storage I/O characteristics, so you must measure this in your own environment before adopting it.
Strategy by Scenario
| Scenario | Recommended Strategy |
|---|---|
| Hundreds of GB to TB-scale OLTP with low daily change rate (roughly under 10–20%) | Full backup weekly + daily incrementals |
| High-frequency transaction DB requiring a short RPO | Maintain a short full backup cycle + hourly incrementals as a supplement |
| Large batch workloads where most blocks change daily | A full backup strategy remains simpler and faster |
pg_walsummary — When to Use It
The pg_walsummary CLI is a diagnostic tool that outputs which blocks of which relations are recorded in a summary file. You will rarely need it in day-to-day operations; it is useful in cases like:
- When an incremental backup is larger than expected, to check at the summary-file level which relation had a large volume of changes
- Debugging whether the Summarizer state and the contents of summary files are consistent
- Confirming suspected corruption of a summary file
pg_walsummary $PGDATA/pg_wal/summaries/<filename>The output is a list of relation file nodes and their changed block ranges. See the pg_walsummary section of the official PostgreSQL 17 documentation for the detailed format.
Trade-offs
What You Gain
- Shorter backup windows: Since only changed blocks are transferred, backup time and transfer volume drop noticeably for large databases with a low change rate.
- Self-contained without external tools: A chain backup is possible with just
pg_basebackup+pg_combinebackup. It is now easier to bundle into a single container image. - Built-in integrity verification:
backup_manifestchecksums allow file corruption to be detected at merge time.
What You Accept
Constant WAL Summarizer overhead. Keeping summarize_wal = on means a process continuously parsing WAL is running, and summary files accumulate under pg_wal/summaries/. The CPU overhead is reported to be small but nonzero, and the summary files become an additional management concern. You can control the retention period with wal_summary_keep_time, but setting it too short can cause incrementals based on older backups to fail.
Chain management risk. Recovery is only possible when the chain is complete — from the full backup through every intermediate incremental. If a single incremental in the middle is lost, the entire chain beyond that point is invalidated.
Merge resource requirements. pg_combinebackup requires all files in the chain to be on the same local filesystem, and needs additional space and I/O to hold the merged result. If your backups live in object storage, you must download them locally before merging.
Parallel transfer limitations. pg_basebackup still defaults to single-stream transfer. Block-level parallel transfer is not supported in incremental backup mode — a difference from pgBackRest and WAL-G, which support distributed transfer with their own worker processes. (Note: the -j option for tablespace-level parallelism belongs to pg_dump-family tools, not pg_basebackup — do not confuse the two.)
No built-in retention or automation. Features like "automatically delete backups older than N days" or "periodically validate chain integrity" are not included. You must write these scripts yourself.
Tool Comparison
As of 2026, here is a rough positioning of the main candidates.
| Item | pg_basebackup + pg_combinebackup (PG17) | pgBackRest | Barman | WAL-G |
|---|---|---|---|---|
| Incremental backup | Native support | Own method (mature, long-standing implementation) | Uses PG17 engine and own method | Own method |
| Block-level parallel transfer | Not supported | Supported | Supported | Supported |
| Retention policy / scheduling | Separate script required | Built-in | Built-in | Built-in |
| Multi-instance central management | Not supported | Supported | Strength | Moderate |
| Object storage integration | No direct support | Supported | Supported | Strength |
| External dependencies | None | Required | Required | Required |
Check each tool's official repository directly for its latest supported scope, license, and maintenance status.
When to Use It and When Not To
From a reviewer's perspective, the breakdown looks like this.
Situations where it is viable
- Single or small number of instances, OLTP databases with a low daily change rate
- Organizations where adopting an external tool carries significant burden (audit, security review, etc.)
- Environments — such as Kubernetes operators or custom backup controllers — where you want to compose using only PostgreSQL standard tools
- Teams that already have script-based backup orchestration and just want to swap in one tool
Situations where you are better off waiting
- Environments requiring central management of dozens or more instances — the absence of retention policies and a catalog is a decisive drawback
- Workloads where the daily change rate roughly exceeds half of the data — the benefit of incrementals disappears and you are left with only chain management overhead
- Pipelines that must write backups directly to object storage (S3, GCS, etc.) — only local filesystems are supported natively
- Very large databases where parallel transfer bandwidth is the deciding factor for backup SLA
If you are evaluating incremental backup adoption, start by checking pg_get_wal_summarizer_state() to confirm the Summarizer is keeping up with WAL without strain, then run a full merge and recovery cycle in staging with production-scale data. That is the only reliable evidence. The merge time in your environment — not a vendor benchmark — is what determines whether to adopt it.
References
- PostgreSQL 17 Official Docs — pg_basebackup
- PostgreSQL 17 Official Docs — pg_combinebackup
- PostgreSQL 17 Official Docs — pg_walsummary
- PostgreSQL 17 Official Docs — Backup and Restore
- PostgreSQL 17 Official Docs — WAL Configuration (summarize_wal, wal_summary_keep_time)
- PostgreSQL 17 Official Docs — Recovery Configuration
- Why PostgreSQL 17's Incremental Backup Feature is a Game-Changer — EDB
- Incremental Backup Challenges and Solutions for PostgreSQL 17 — EDB
- Waiting for Postgres 17: Incremental base backups — pganalyze
- PostgreSQL 17: Incremental Backup — Mydbops