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_dump is 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_combinebackup rebuilds a full data directory, and pg_verifybackup checks 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-cloud for 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:

  1. 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).
  2. A continuous WAL archive: every completed WAL segment copied out by archive_command or archive_library, or streamed with pg_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.
  3. A recovery procedure: restore the base backup, set restore_command and a recovery target (recovery_target_time, _lsn, _xid or _name), create recovery.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.info and backup.info are created once by stanza-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 restore unless allow-root is 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

  1. Write the restore promise: PITR window, RPO, RTO, undeletable copy, last proven date.
  2. Turn on WAL archiving before the first base backup and alert on failed_count and archive age.
  3. Set archive_timeout or stream WAL so a quiet server still meets the RPO.
  4. Enable summarize_wal if you plan built-in or Barman block-level incrementals.
  5. Encrypt client-side and keep keys in a secret store, with a tested recovery path for the keys.
  6. Keep at least one copy in a different building or region, with versioning or object lock.
  7. Pin versions: pgBackRest 2.59.3, WAL-G v3.0.9, Barman 3.20.1 or later.
  8. Run the pgBackRest weak subkey check on every encrypted repository created before 2.59.3.
  9. Verify backups with the tool (verify, wal-verify, verify-backup) and by restoring.
  10. Practise a timed PITR restore every month and record the result.

Common mistakes

  1. Treating the streaming replica as the backup. It replicates the mistake within a second.
  2. Relying on pg_dump alone and calling it PITR.
  3. Writing an archive_command that returns zero even when the copy fails, or that overwrites existing files.
  4. Taking pg_basebackup incrementals and deleting the full backup they depend on.
  5. Restoring a cluster with archiving still on, so the restored server writes a new timeline into the production archive.
  6. Giving the database host credentials that can delete every backup, with no versioning or object lock behind them.
  7. Upgrading pgBackRest to 2.59.3 and assuming that repairs an existing weak subkey.
  8. Running Barman 3.20.0 or older with snapshot backups and a separate, less privileged catalogue writer.
  9. Ignoring timezones in recovery_target_time; always include the offset, such as +05:30.
  10. 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 20 plus an invented rides table of 300,000 rows: 331 MB.
  • WAL left the server three ways: one archive_command that copied each segment to a plain directory (with test ! -f), then ran pgbackrest archive-push and wal-g wal-push; Barman streamed WAL with pg_receivewal and 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:

  1. All four did a correct PITR: same target, same stopping transaction, same data. The differences are around the restore.
  2. pgBackRest and Barman wrote the recovery settings; for built-in and WAL-G I wrote them by hand. Barman also set archive_command = false and flagged the copied unix_socket_directories for review.
  3. The chain is real. pg_combinebackup bb-incr alone failed with “is an incremental backup, but the first backup should be a full backup”. Nothing in PostgreSQL stops you deleting bb-full.
  4. 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.
  5. 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.
  6. Warnings worth reading: wal-verify could not parse timelines from .backup history files but reported both checks OK; Barman flagged my local_staging_path as deprecated in favour of staging_path and staging_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_dump as 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 --incremental and pg_combinebackup in PostgreSQL 17 and 18.
  • Re-learn recovery settings, timelines and archive_mode on 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

pgBackRest

WAL-G

Barman

Kubernetes operators

Security records

Internal

comments powered by Disqus

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 more

Durable 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 more

PostgreSQL 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