PostgreSQL backups in 2026: revisit pgBackRest, WAL-G and Barman before you trust another restore
Picture a company in Bhopal that runs a cab and auto-rickshaw dispatch and fare platform for Bhopal, Indore, Ujjain and Dewas (an invented example, not a real client). The core is one PostgreSQL 18 cluster, a primary and a Patroni-managed streaming replica, plus a nightly pg_dump at 1 AM IST copied offsite. One Friday at 7:42 PM IST, a fare correction script with the wrong WHERE clause resets the fare on every ride booked that day. The replica applies the change within a second, as designed. The only clean copy is last night’s dump, eighteen hours old, and the team spends the weekend rebuilding fares from payment gateway reports.
On Monday the CTO sets a new target: restore to any second in the last 14 days, lose at most five minutes of data, and prove it every month. The DBA wants pgBackRest. The platform engineer, who is moving the cluster to Kubernetes, wants Barman through the CloudNativePG plugin. A Go developer has used WAL-G at a previous job. And someone asks a fair question: PostgreSQL 17 and 18 ship incremental backup with pg_basebackup and pg_combinebackup, so why install anything at all?
This post compares those four options as they stand on 18 October 2026: WAL archiving, full and incremental backups, compression and encryption, object storage, retention, verification, point-in-time recovery (PITR), standby backups, Kubernetes fit, licence and maintainer health. File-level tools such as restic, BorgBackup and Kopia were covered in the backups post; this post is about database-aware physical backups. And as the Friday incident shows, a replica is not a backup: failover is covered in the PostgreSQL high availability post.
Facts come from GitHub releases, LICENSE files, official documentation, PostgreSQL release notes, the CVE record and OSV, and a drill on this Linux box with PostgreSQL 18.6, pgBackRest 2.59.3, WAL-G v3.0.9 and Barman 3.20.1 restoring one database to the same target second. It is not a speed benchmark.
The short version
- Physical backup plus continuous WAL archiving is what gives you PITR. A
pg_dumpis a logical copy; the PostgreSQL docs state plainly that dumps cannot be used with continuous archiving. - PostgreSQL built-in:
pg_basebackup --incremental(since PostgreSQL 17) copies only changed blocks using WAL summaries,pg_combinebackuprebuilds a full data directory, andpg_verifybackupchecks it against the manifest. There is no catalogue, retention or WAL archive management. You own the chain. - pgBackRest is at 2.59.3 (4 October 2026, 2:32 PM IST), MIT, written in C. Block incremental, parallel processes, several repositories (posix, S3, Azure, GCS, SFTP), client-side AES-256,
verify, delta restore. In April 2026 its maintainer announced it would stop; by May a coalition of sponsors had funded it again. - WAL-G is at v3.0.9 (20 August 2026, 7:56 PM IST), Apache 2.0, written in Go. Object storage first, delta backups, several compression and encryption choices, and other databases too. Releases are infrequent; commits are not.
- Barman is at 3.20.1 (30 September 2026, 3:28 AM IST), GPL-3.0, owned by EnterpriseDB, Python 3.12+. A backup server model with catalogue and retention policies, PostgreSQL 17+ block-level incremental,
barman-cloudfor object storage, and the engine behind CloudNativePG’s Barman Cloud Plugin. - Security: Barman 3.20.1 fixes CVE-2026-93853 (snapshot deletion trusting the backup catalogue, CVSS 4.0 score 7.2). pgBackRest 2.59.3 fixes possible weak encryption subkeys. WAL-G’s only OSV entry is the 2021 CVE-2021-38599 (fixed in 1.1).
- In the drill, all four restored to the same target second and the row inserted two seconds later was absent every time.
Where things stand on 18 October 2026
Dates are GitHub release published_at converted from UTC to IST (UTC+5:30), except PostgreSQL, which publishes dates only. Licences are from the LICENSE files and project licence pages.
Project
Release
Date (IST)
Notes
Licence
PostgreSQL
18.6
13 August 2026
Current major; supported until November 2030
PostgreSQL Licence
PostgreSQL
17.11
13 August 2026
First major with incremental pg_basebackup
PostgreSQL Licence
PostgreSQL
19 RC 1
15 October 2026
GA planned for 19 November 2026; not for production
PostgreSQL Licence
pgBackRest (pgbackrest/pgbackrest)
2.59.3
4 October 2026, 2:32 PM
Weak encryption subkey and salt fix
MIT
pgBackRest
2.59.2
27 September 2026, 5:12 PM
PostgreSQL 19beta4 support, S3 and Azure fixes
MIT
pgBackRest
2.59.0
20 July 2026, 2:13 PM
PostgreSQL 19 support; only restore may run as root by default
MIT
WAL-G (wal-g/wal-g)
v3.0.9
20 August 2026, 7:56 PM
PG15 base backup protocol support, timeline switch check, libsodium 1.0.22
Apache 2.0
WAL-G
v3.0.8
21 January 2026, 7:49 PM
Direct I/O reader for PostgreSQL, Prometheus exporter, Alibaba OSS
Apache 2.0
Barman (EnterpriseDB/barman)
3.20.1
30 September 2026, 3:28 AM
Fixes CVE-2026-93853
GPL-3.0
Barman
3.20.0
28 August 2026, 2:19 AM
Python 3.12+ required; AWS S3 SSE-C
GPL-3.0
Barman Cloud Plugin (cloudnative-pg/plugin-barman-cloud)
v0.15.1
30 September 2026, 7:04 PM
Bundles Barman 3.20.1; needs CloudNativePG 1.26+
Apache 2.0
CloudNativePG
v1.30.1
23 September 2026, 7:05 PM
Native barmanObjectStore deprecated since 1.26
Apache 2.0
Crunchy Postgres for Kubernetes (PGO)
v6.0.2
3 June 2026, 12:53 AM
Backups through pgBackRest
Apache 2.0
GitHub stars on 18 October 2026: pgBackRest about 4,447, WAL-G about 4,311, Barman about 3,260. Stars measure attention, not quality.
Timeline
Date (IST)
Event
12 November 2013, 2:38 AM
pgbackrest/pgbackrest repository created
17 August 2015, 3:19 PM
EnterpriseDB/barman repository created on GitHub (Barman’s copyright line starts in 2011)
14 April 2016
pgBackRest v1.00: new repository format and configuration scheme
22 June 2017, 11:46 PM
wal-g/wal-g repository created; the docs describe WAL-G as the successor of WAL-E
23 February 2018
pgBackRest v2.00: archive-push partly rewritten in C
22 May 2023
pgBackRest v2.46: block incremental backup and SFTP storage
17 March 2024, 3:34 PM
WAL-G v3.0.0
22 August 2024, 7:09 PM
Barman 3.11.0: PostgreSQL 17 block-level incremental backups
26 September 2024
PostgreSQL 17: pg_basebackup --incremental, pg_combinebackup, WAL summarisation
25 September 2025
PostgreSQL 18: pg_combinebackup --link, pg_verifybackup for tar backups, checksums on by default in initdb
27 April 2026
pgBackRest news: “pgBackRest Is No Longer Being Maintained”
18 May 2026
pgBackRest news: “pgBackRest Will Continue!”, funded by a coalition of sponsors
30 September 2026, 2:25 AM
CVE-2026-93853 published by EDB; Barman 3.20.1 an hour later
4 October 2026
pgBackRest 2.59.3 and the weak encryption subkeys notice
15 October 2026
PostgreSQL 19 RC 1
First question: what promise are you making?
Before choosing a tool, write the restore promise in one sentence. If you cannot fill every blank, no tool will save you.
We can restore the [cluster] to any second in the last [N] days, losing at most [RPO] minutes of committed data, within [RTO] hours, from a copy that someone holding our database credentials cannot delete, and we last proved it on [date].
Three building blocks deliver that promise in PostgreSQL:
- A base backup: a physical copy of the data directory taken while the server runs, with a backup label and a backup manifest (manifests exist since PostgreSQL 13).
- A continuous WAL archive: every completed WAL segment copied out by
archive_commandorarchive_library, or streamed withpg_receivewal. The PostgreSQL docs require the archive command to return zero only on success and to refuse to overwrite an existing file with different contents. - A recovery procedure: restore the base backup, set
restore_commandand a recovery target (recovery_target_time,_lsn,_xidor_name), createrecovery.signal, start the server, and let it replay WAL up to the target.
RPO depends on how quickly WAL leaves the server; a quiet server may not fill a segment for a long time, so the docs suggest an archive_timeout of about a minute, or streaming. RTO depends on how much data is copied back and how much WAL is replayed, which is why incrementals matter: shorter replay without a full copy every night.
What each tool actually is
PostgreSQL built-in: pg_basebackup, pg_combinebackup and pg_verifybackup
pg_basebackup takes a base backup over the replication protocol, as plain files or tar, optionally compressed on the client or the server with gzip, LZ4 or Zstandard, and writes a backup manifest. With -X stream it includes the WAL needed to make that one backup consistent; for PITR you still need a separate WAL archive.
Since PostgreSQL 17, pg_basebackup --incremental=<earlier manifest> asks the server for only the blocks changed since that backup. The server knows which blocks changed from WAL summaries, which the walsummarizer process writes when summarize_wal = on. The docs warn that summaries must cover the whole LSN range since the earlier backup, otherwise the incremental fails. An incremental backup cannot be started directly: pg_combinebackup must merge the full backup and every incremental after it into a new data directory. PostgreSQL 18 added --link (hard links instead of copies) and pg_verifybackup support for tar format.
What is missing is everything around the chain. The docs say PostgreSQL has no built-in mechanism to track which backups later incrementals still need. No retention, catalogue, WAL pruning, encryption or object storage. A good engine, not a system.
pgBackRest 2.59.3
pgBackRest is a C program with one configuration file and a stanza per cluster. archive-push and archive-get plug into archive_command and restore_command, optionally asynchronous with a local spool. Backups are full, differential or incremental. With repo-bundle and repo-block enabled, incrementals store only changed blocks of changed files, using block maps from earlier backups.
Repositories can be posix, CIFS, S3, Azure Blob, GCS or SFTP, several at once: say a local repository with short retention and an S3 one with long retention. Encryption is aes-256-cbc, always client-side. Compression is gz by default, with bz2, lz4 and zst. process-max parallelises copy and compression. Every file gets a checksum, page checksums are validated when enabled, interrupted backups can resume, and verify rechecks the repository. Delta restore rewrites only files that differ, and backup-standby takes most files from a replica. Version 2.54.0 added reading a versioned object store repository as of a target time, useful after deletion or overwrite.
Maintenance deserves its own paragraph. On 27 April 2026 the maintainer, David Steele, posted that pgBackRest was no longer being maintained: since Crunchy Data was sold he had not found a role or enough sponsorship to continue. On 4 May he wrote that funding looked all but certain, and on 18 May announced “pgBackRest Will Continue!”, funded by a coalition of sponsors so that one acquisition would no longer decide its fate. Recent release announcements thank AWS, Supabase, pgEdge, Tiger Data, Percona, Eon, Xata, Dalibo and Data Egret; Crunchy Data is listed as a past sponsor. Four releases have followed (2.59.0 to 2.59.3), reviewed by several people besides the maintainer. The scare ended well, but one person had been the bus factor for a tool much of the PostgreSQL world relies on.
WAL-G v3.0.9
WAL-G is a single Go binary configured through environment variables or a config file. wal-push and wal-fetch go into archive_command and restore_command. backup-push takes a base backup, backup-fetch restores one, and delete retain handles retention, with --permanent backups excluded. The docs describe it as the successor of WAL-E, and the LICENSE carries a 2017 Citus Data copyright.
Storage is S3 and compatible stores, GCS, Azure, Swift, Alibaba OSS, SSH or a local path. Compression is LZ4, LZMA, Zstandard or Brotli (or none since 3.0.8). Encryption can use libsodium, OpenPGP, an envelope PGP key in a KMS, or client-side AWS or Yandex Cloud KMS. Upload and download concurrency are configurable, and wal-fetch prefetches WAL ahead of replay.
Incrementals are delta backups: set WALG_DELTA_MAX_STEPS and WAL-G compares page LSNs and file modification times with the previous backup; backup-fetch composes base and deltas automatically. Also useful: wal-verify integrity timeline, page checksum checks with backup-push --verify, partial restore of chosen databases, and catchup backups for a lagging replica. Remote backups over the BASE_BACKUP protocol do not support deltas, the docs note.
WAL-G also covers MySQL and MariaDB, MS SQL Server, and in beta MongoDB and Redis, so read release notes for the PostgreSQL parts. Releases are infrequent (January and August 2026), but the default branch had commits from about twenty authors in the 90 days to 18 October 2026.
Barman 3.20.1
Barman, the Backup and Recovery Manager from EnterpriseDB, assumes a dedicated backup server that pulls from one or more PostgreSQL servers. backup_method = rsync uses the low-level backup API and rsync over SSH, with file-level incrementals through reuse_backup = link. backup_method = postgres uses pg_basebackup over a streaming connection and, since 3.11, PostgreSQL 17+ block-level incrementals with barman backup --incremental <parent>. WAL arrives through archive_command (barman-wal-archive) or streaming with pg_receivewal and a replication slot.
Retention is a policy such as REDUNDANCY 4 or RECOVERY WINDOW OF 14 DAYS, with minimum_redundancy as a floor and barman keep to pin a backup. Since 3.11, retention counts only full backups, because an incremental is useless without its chain. Barman can encrypt backups and WAL with GPG; since 3.15 that works with incrementals and compression too. barman restore (alias recover) restores locally or to a remote host over SSH, writes the recovery settings, and for incrementals runs pg_combinebackup in a staging area. verify-backup runs pg_verifybackup against the backup manifest. Rsync backups can copy files with several parallel jobs.
For object storage, the barman-cloud-* commands run on the database host and write to S3, Azure or GCS without a Barman server; cloud volume snapshots are another option. 3.18 added experimental block-level incrementals streamed to cloud storage, and 3.19 added restoring them with barman restore and barman cloud-wal-restore. Barman is owned by EnterpriseDB UK Limited, which requires contributors to sign a copyright assignment. Releases are regular: seven in the twelve months to 18 October 2026.
Comparison tables
WAL archiving and the backup chain
Built-in
pgBackRest
WAL-G
Barman
WAL out of the server
Your own archive_command, archive_library or pg_receivewal
archive-push, optional async with spool and batch size
wal-push, or wal-receive for streaming
barman-wal-archive, or streaming via pg_receivewal and slot
WAL back during restore
Your own restore_command
archive-get, written into the restored config
wal-fetch with prefetch
Copies WAL during restore, or get-wal on demand
Check that archiving works
Watch pg_stat_archiver yourself
check
wal-verify
check, check-wal-archive
Tracks the chain and prunes WAL
No
Yes
Yes (delete retain)
Yes
Backup types and incrementals
Built-in
pgBackRest
WAL-G
Barman
Full
Yes
Yes
Yes
Yes
Differential
Point --incremental at the full’s manifest
Yes (--type=diff)
WALG_DELTA_ORIGIN=LATEST_FULL
--incremental latest-full
Incremental
Block-level, PG17+, needs summarize_wal
File-level, or block-level with repo-block
Delta by page LSN
File-level with rsync, or block-level with PG17+
Restore of an incremental
pg_combinebackup by hand
Automatic
Automatic (backup-fetch)
Automatic, pg_combinebackup in staging
Parallelism, compression and encryption
Built-in
pgBackRest
WAL-G
Barman
Parallel copy
No
process-max
Upload and download concurrency
parallel_jobs (--jobs); --parallel for cloud WAL
Compression
gzip, LZ4, Zstandard, on client or server
gz, bz2, lz4, zst
LZ4, LZMA, Zstandard, Brotli, none
WAL: gzip, bzip2, lz4, xz, zstd, pigz; backups: gzip, lz4, zstd (postgres method)
Client-side encryption
No
AES-256-CBC
libsodium, OpenPGP, KMS envelope
GPG (server model)
Server-side encryption on cloud stores
Not applicable
S3 SSE-KMS
S3 SSE and SSE-C
barman-cloud AES256, aws:kms, SSE-C (3.20.0)
Repositories, object storage and retention
Built-in
pgBackRest
WAL-G
Barman
Local or NFS
Yes
Yes
Yes
Yes (backup server disk)
S3, Azure, GCS
No
Yes
Yes
Yes, via barman-cloud and cloud directories
Other
No
SFTP, CIFS
Swift, Alibaba OSS, SSH
Cloud volume snapshots (AWS, Azure, GCP)
Second copy
Your own scripts
Several repositories at once (repo1, repo2)
Copy between storages (backup-copy, st copy)
Geographical redundancy between Barman servers
Retention
None
Count or days, separate for diff and WAL
delete retain and related targets, permanent backups
Redundancy or recovery window, minimum redundancy, keep
Verification, restore and PITR
Built-in
pgBackRest
WAL-G
Barman
Verify backup files
pg_verifybackup
verify, checksums per file
wal-verify, page checksum check on push
verify-backup, check-backup
Target types
All PostgreSQL targets, set by hand
time, xid, lsn, name, immediate
Set by hand in config
time, xid, lsn, name, timeline, immediate
Delta restore into existing directory
No
Yes (--delta)
Not documented
Yes (--delta-restore, 3.16)
Partial restore
No
db-include
--restore-only
Not documented
Standby backups, Kubernetes fit and governance
Built-in
pgBackRest
WAL-G
Barman
Backup from standby
Yes (incremental on a standby may fail if no new restartpoint)
backup-standby
Yes, watches for timeline change
Yes, with primary_conninfo for WAL switch
Kubernetes
Not a fit alone
Built into Crunchy PGO
Run it in your own image or sidecar
CloudNativePG Barman Cloud Plugin
Language
C (part of PostgreSQL)
C
Go
Python 3.12+
Licence
PostgreSQL Licence
MIT
Apache 2.0
GPL-3.0, copyright assigned to EDB
Governance
PostgreSQL Global Development Group
Lead maintainer plus sponsor coalition since May 2026
wal-g organisation on GitHub, many contributors
Owned by EnterpriseDB
CloudNativePG’s docs say native barmanObjectStore has been deprecated since 1.26 in favour of the Barman Cloud Plugin, though it remains the default for backward compatibility; plan the migration. On Kubernetes, your operator often chooses the backup tool for you.
Worked example for the Bhopal scenario
The Bhopal team (invented) has a 600 GB cluster on two VMs, a MinIO cluster in another building and an S3 bucket in Mumbai. They chose pgBackRest for the VMs because it gives them two repositories, client-side encryption and block incremental in one tool, and because the maintenance question had a clear answer by May 2026. They will revisit when the cluster moves to CloudNativePG, where the Barman Cloud Plugin is the operator’s supported path.
Step 1. Write the promise: 14 days of PITR, RPO five minutes, RTO two hours, one copy that the database host cannot delete, a restore drill on the first Monday of every month.
Step 2. Configure two repositories and archiving. A trimmed version:
# /etc/pgbackrest/pgbackrest.conf (passphrases come from a secret store, not this file)
[global]
repo1-type=s3
repo1-path=/pgbackrest
repo1-s3-bucket=fares-pg-local
repo1-s3-endpoint=minio.internal.example
repo1-s3-region=us-east-1
repo1-s3-uri-style=path
repo1-retention-full=2
repo1-cipher-type=aes-256-cbc
repo2-type=s3
repo2-path=/pgbackrest
repo2-s3-bucket=fares-pg-mumbai
repo2-s3-endpoint=s3.ap-south-1.amazonaws.com
repo2-s3-region=ap-south-1
repo2-retention-full=3
repo2-cipher-type=aes-256-cbc
repo1-bundle=y
repo1-block=y
repo2-bundle=y
repo2-block=y
compress-type=zst
process-max=4
archive-async=y
spool-path=/var/spool/pgbackrest
start-fast=y
[fares]
pg1-path=/var/lib/postgresql/18/main
# postgresql.conf
# archive_mode = on
# archive_command = 'pgbackrest --stanza=fares archive-push %p'
# archive_timeout = 60
Step 3. Schedule a weekly full and daily incrementals, then run pgbackrest --stanza=fares check and alert on pg_stat_archiver.failed_count and on the age of last_archived_time.
Step 4. Make the Mumbai copy hard to destroy. Turn on bucket versioning (or object lock where the store supports it) with lifecycle rules longer than the retention, so deletes made with the database host’s credentials can be undone. pgBackRest can read a versioned repository as it was at a target time (--repo-target-time, since 2.54.0).
Step 5. Practise. Monthly, restore to a scratch VM with --type=time --target="<timestamp with +05:30>" --target-action=promote, start it with archive_mode=off so it cannot write into the real archive, check known rows, and record the time taken. That number is your RTO.
Security and hygiene
On 18 October 2026 the GitHub repository security advisories API returned no published advisories for pgbackrest/pgbackrest, wal-g/wal-g or EnterpriseDB/barman. That does not mean nothing happened this year.
- Barman, CVE-2026-93853 (published 30 September 2026, 2:25 AM IST, CVSS 4.0 score 7.2). When deleting a snapshot backup, Barman trusted the snapshot IDs in
backup.info, so someone who could overwrite the catalogue but not delete snapshots could make Barman delete unrelated snapshots on AWS, Azure or Google Cloud with its own credentials. Affected from 3.4.0 (Google Cloud), 3.6.0 (Azure) and 3.7.0 (AWS) up to 3.20.0; fixed in 3.20.1, which checks the snapshot name with the provider first. The CloudNativePG plugin v0.15.1 bundles 3.20.1. - pgBackRest weak encryption subkeys (4 October 2026). Before 2.59.3, if OpenSSL could not get random data from the operating system, subkeys and salts could silently come from memory. Only encrypted repositories are affected. Subkeys in
archive.infoandbackup.infoare created once bystanza-create, so upgrading does not replace a weak one. The notice gives a shell check (six or more zero bytes means weak); the fix is a new stanza in a new repository. - WAL-G, CVE-2021-38599 (GHSA-vrmr-f2qh-3hhf). Before 1.1, builds without libsodium silently ignored the libsodium key and uploaded cleartext backups. Old, but a good lesson: check that encryption is really applied, not just configured.
Habits that matter more than CVE counts:
- Keep passphrases and keys away from the backup bucket, and make sure you can recover them. A backup you cannot decrypt is not a backup.
- Limit what the database host’s credentials can destroy: versioning or object lock on the bucket, deletion rights only where expiry runs, and audit logs on the bucket.
- pgBackRest 2.59.0 refuses to run as root except for
restoreunlessallow-rootis set; keep it that way. - WAL contains everything in the database. Archive storage needs the same access controls as the database itself.
Decision guide
Situation
Lean towards
Why
VMs or bare metal, need encryption and several repositories
pgBackRest
Block incremental, multi-repo, delta restore, verify
Cloud VMs, object storage only, Go-friendly team, maybe MySQL too
WAL-G
One binary, object storage first, delta backups, broad database coverage
Central backup server for many clusters, EDB support
Barman
Catalogue, recovery window policies, GPG, snapshots
CloudNativePG on Kubernetes
Barman Cloud Plugin
The operator’s supported path; native barmanObjectStore is deprecated
Crunchy PGO
pgBackRest
Built into the operator
Small database, few changes, strong in-house scripting
Built-in pg_basebackup plus an archive
No extra software, but you own retention and verification
Strict permissive licence policy
pgBackRest (MIT) or WAL-G (Apache 2.0)
Barman is GPL-3.0; fine to run internally, but check if you redistribute
Bhopal (invented) pick. pgBackRest with a local MinIO repository and an encrypted Mumbai S3 repository on the VMs, monthly restore drills, and a planned move to the Barman Cloud Plugin with the CloudNativePG migration.
Checklist
- Write the restore promise: PITR window, RPO, RTO, undeletable copy, last proven date.
- Turn on WAL archiving before the first base backup and alert on
failed_countand archive age. - Set
archive_timeoutor stream WAL so a quiet server still meets the RPO. - Enable
summarize_walif you plan built-in or Barman block-level incrementals. - Encrypt client-side and keep keys in a secret store, with a tested recovery path for the keys.
- Keep at least one copy in a different building or region, with versioning or object lock.
- Pin versions: pgBackRest 2.59.3, WAL-G v3.0.9, Barman 3.20.1 or later.
- Run the pgBackRest weak subkey check on every encrypted repository created before 2.59.3.
- Verify backups with the tool (
verify,wal-verify,verify-backup) and by restoring. - Practise a timed PITR restore every month and record the result.
Common mistakes
- Treating the streaming replica as the backup. It replicates the mistake within a second.
- Relying on
pg_dumpalone and calling it PITR. - Writing an
archive_commandthat returns zero even when the copy fails, or that overwrites existing files. - Taking
pg_basebackupincrementals and deleting the full backup they depend on. - Restoring a cluster with archiving still on, so the restored server writes a new timeline into the production archive.
- Giving the database host credentials that can delete every backup, with no versioning or object lock behind them.
- Upgrading pgBackRest to 2.59.3 and assuming that repairs an existing weak subkey.
- Running Barman 3.20.0 or older with snapshot backups and a separate, less privileged catalogue writer.
- Ignoring timezones in
recovery_target_time; always include the offset, such as+05:30. - Measuring backup duration and never measuring restore duration.
A drill I ran on 18 October 2026
Shared Linux VM (Debian 13, 8 vCPU, 15 GiB RAM), no Docker or Kubernetes, port 8766 left alone. Runs between about 2:22 PM and 2:26 PM IST. Everything ran on one box against one local disk, so durations show what each step involves, not which tool is faster.
Setup:
- PostgreSQL 18.6 and pgBackRest 2.59.3 from PGDG Debian packages, WAL-G v3.0.9 release binary
wal-g-pg-24.04-amd64, Barman 3.20.1 wheel from PyPI in a Python 3.13.5 virtual environment. All SHA-256 checksums matched the published values. Debian’s rsync 3.4.1 was added after the first Barman restore failed without it. - One cluster with data checksums (the PostgreSQL 18 default) and
summarize_wal = on.pgbench -i -s 20plus an inventedridestable of 300,000 rows: 331 MB. - WAL left the server three ways: one
archive_commandthat copied each segment to a plain directory (withtest ! -f), then ranpgbackrest archive-pushandwal-g wal-push; Barman streamed WAL withpg_receivewaland a slot. Chaining three tools in one command was a drill convenience, not a recommendation. - pgBackRest: posix repository,
aes-256-cbc, zst, bundles and block incremental. WAL-G: local file storage, Zstandard, libsodium key,WALG_DELTA_MAX_STEPS=6. Barman:backup_method = postgres, no backup compression or encryption.
Then: full backups with all four, an update of 50,000 of the 2,000,000 pgbench_accounts rows plus 20,000 new rides, and incrementals with all four.
Step
Built-in
pgBackRest
WAL-G
Barman
Full backup, wall time
1.72 s
1.49 s
1.75 s
6.30 s (includes --wait for WAL)
Full backup, stored
346 MB, plain files
19.3 MB (zst, encrypted)
20 MB (Zstandard, encrypted)
345.8 MiB, uncompressed
Incremental, wall time
1.15 s
0.95 s
1.20 s
2.15 s
Incremental, stored
23 MB
891.4 KB
1.1 MB
19.8 MiB (“resources saved” 94.42%)
Verification
pg_verifybackup OK on both
verify OK
wal-verify integrity timeline OK
verify-backup OK on both, check-backup OK
pgbench rows are mostly blank-padded filler and compress far better than real tables, so treat the 345 MB to 20 MB ratio as an artefact of synthetic data. The useful comparison is the incremental column: block-level change tracking kept every incremental small after a 2.5 percent update.
PITR markers: a row before-target committed at 2:23:05.535 PM IST, target time 2:23:07.544 PM IST, a row after-target committed at 2:23:09.552 PM IST, then a WAL switch. Each restore went to its own directory and port, started with archive_mode=off.
Restore
Restore step
Start, replay, promote
Markers present
Rides
Timeline
Built-in: pg_combinebackup full + incremental, cp from archive
0.44 s
0.32 s
before-target only
320,000
2
pgBackRest restore --type=time
1.27 s
0.32 s
before-target only
320,000
2
WAL-G backup-fetch LATEST + wal-fetch
0.80 s
0.83 s
before-target only
320,000
2
Barman restore --target-time (second attempt)
0.63 s
0.22 s
before-target only
320,000
2
All four server logs show the same line: recovery stopping before commit of transaction 771, time 2026-10-18 14:23:09.552316+05:30. The before-target row was inserted after the incremental backups finished, so finding it proves WAL replay worked, not just the base copy. All four also had the 50,000 updated accounts captured by the incremental.
The built-in sequence, trimmed:
pg_basebackup -D bb-full -c fast -X none --manifest-checksums=SHA256
# ... changes ...
pg_basebackup -D bb-incr -c fast -X none --incremental=bb-full/backup_manifest
pg_combinebackup bb-full bb-incr -o restore/builtin
pg_verifybackup -n restore/builtin
touch restore/builtin/recovery.signal
# appended to restore/builtin/postgresql.auto.conf:
# restore_command = 'cp /path/to/wal-archive/%f %p'
# recovery_target_time = '2026-10-18 14:23:07.543999+05:30'
# recovery_target_action = 'promote'
pg_ctl -D restore/builtin -o "-p 15446 -c archive_mode=off" start
What the drill showed:
- All four did a correct PITR: same target, same stopping transaction, same data. The differences are around the restore.
- pgBackRest and Barman wrote the recovery settings; for built-in and WAL-G I wrote them by hand. Barman also set
archive_command = falseand flagged the copiedunix_socket_directoriesfor review. - The chain is real.
pg_combinebackup bb-incralone failed with “is an incremental backup, but the first backup should be a full backup”. Nothing in PostgreSQL stops you deletingbb-full. - Environment gaps appear only at restore time. Barman’s backups and verification passed, but the first restore failed with “rsync not in PATH”. A monthly restore drill catches this. A backup dashboard does not.
- Encryption was really on where configured. pgBackRest files began with the OpenSSL
Salted__header and WAL-G objects were not plain Zstandard. The pgBackRest weak subkey check from the 4 October notice found no zero bytes in either subkey. - Warnings worth reading:
wal-verifycould not parse timelines from.backuphistory files but reported both checks OK; Barman flagged mylocal_staging_pathas deprecated in favour ofstaging_pathandstaging_location.
What this box could not test: S3 or other object storage, a separate repository host over SSH or TLS, backups from a standby, parallelism on a large database, barman-cloud and snapshot backups, the CloudNativePG plugin and Crunchy PGO, and PostgreSQL 19. No PostgreSQL server or Barman WAL receiver was left running afterwards. Scripts, logs and timings are under drill45/.
Unlearn
- Unlearn “the backup job is green, so we are safe”.
- Unlearn the replica as a backup.
- Unlearn
pg_dumpas a recovery plan for a busy OLTP database. - Unlearn that built-in incremental backup means you no longer need a backup tool.
- Unlearn that upgrading a tool fixes the keys and catalogues it created earlier.
Re-learn
- Re-learn the three building blocks: base backup, continuous WAL archive, recovery target.
- Re-learn WAL summaries,
pg_basebackup --incrementalandpg_combinebackupin PostgreSQL 17 and 18. - Re-learn recovery settings, timelines and
archive_modeon restored servers. - Re-learn maintainer health as part of tool choice: releases, reviewers, sponsors, ownership.
- Re-learn RTO as a measured number from a restore you actually ran.
Closing
PostgreSQL backups in 2026 are less about the copy and more about the promise. PostgreSQL 17 and 18 gave the core a good block-level incremental engine, but no catalogue, retention or object storage. pgBackRest has the widest standalone feature set in these tables and is sponsor-funded after an April scare; take 2.59.3 and run the subkey check. WAL-G is the object-storage-first Go option that also covers other databases. Barman brings a backup server model, EDB ownership and the CloudNativePG plugin; take 3.20.1. In the drill all four stopped at the same transaction; the differences were in setup, chain handling and one missing rsync. Learn the three building blocks. Unlearn the green backup job. Re-learn incremental chains. Practise a timed restore to a target second. Apply it this month, before you trust another restore.
Sources
PostgreSQL
- Continuous archiving and point-in-time recovery
- pg_basebackup
- pg_combinebackup
- pg_verifybackup
- WAL configuration, archiving, recovery targets and WAL summarisation
- PostgreSQL 17 release notes
- PostgreSQL 18 release notes
- PostgreSQL 19 RC 1 announcement
- PostgreSQL versioning policy
- PostgreSQL versions data
pgBackRest
- pgBackRest home
- pgBackRest news (maintenance updates and weak encryption subkeys notice)
- pgBackRest release notes
- pgBackRest configuration reference
- pgBackRest command reference
- pgBackRest user guide
- pgbackrest/pgbackrest on GitHub
- pgBackRest 2.59.3 release
- pgBackRest 2.59.0 release
- pgBackRest LICENSE (MIT)
WAL-G
- wal-g/wal-g on GitHub
- WAL-G v3.0.9 release
- WAL-G v3.0.8 release
- WAL-G documentation
- WAL-G for PostgreSQL
- WAL-G storages
- WAL-G LICENSE (Apache 2.0)
Barman
- EnterpriseDB/barman on GitHub
- Barman 3.20.1 release
- Barman 3.20.0 release
- Barman 3.19.0 release
- Barman 3.18.0 release
- Barman 3.15.0 release
- Barman 3.11.0 release
- Barman concepts
- Barman backups
- Barman WAL archiving
- Barman recovery
- Barman retention policies
- Barman cloud
- Barman cloud snapshot backups
- Barman commands
- Barman licence
- Barman on PyPI
Kubernetes operators
- CloudNativePG backup documentation (1.30)
- CloudNativePG v1.30.1 release
- Barman Cloud Plugin documentation
- Barman Cloud Plugin v0.15.1 release
- Crunchy PGO backup configuration
- Crunchy PGO v6.0.2 release
Security records
- CVE-2026-93853 on cve.org
- EDB advisory for CVE-2026-93853
- GHSA-vrmr-f2qh-3hhf (WAL-G, CVE-2021-38599)
- OSV entry GHSA-vrmr-f2qh-3hhf
Internal
Releted Posts
Distributed SQL in 2026: revisit CockroachDB, YugabyteDB and TiDB before you shard or go multi-region
Picture a mutual fund and stockbroking platform in Mumbai (an invented example, not a real client). Its order book and client ledger run on PostgreSQL 16 with Patroni across three VMs in one cloud region, and a rewards service runs on MySQL 8.
Read moreDurable execution in 2026: revisit Temporal, Restate and DBOS before you hand-roll retries, sagas and cron jobs
Picture an online pharmacy and diagnostics booking company in Ahmedabad (an invented example, not a real client). An order goes through five steps: hold the medicine stock, take the UPI payment, book a courier slot, send the customer an SMS, and, if the courier cannot pick up within four hours, release the stock and refund.
Read morePostgreSQL high availability: revisit Patroni and CloudNativePG before your next failover
Picture a lending company in Bengaluru (an invented example, not a real client). Its loan ledger runs on PostgreSQL 16 across three VMs, managed by Patroni with a three-node etcd cluster and HAProxy in front.
Read more