Postgres Backups in 2026: pg_dump vs pgBackRest vs WAL-G vs Barman
I have been asked "how do you back up Postgres?" in almost every infrastructure interview I have sat on either side of, and the honest answer has barely changed in a decade: a nightly pg_dump is not a backup strategy for a production database, and a physical backup you have never restored is a rumour. What has changed is the tooling around it. PostgreSQL 17 added native incremental backups, pgBackRest shipped 2.60 in October, Barman now needs Python 3.12, and the managed services still cap how far back you can go. This post explains the Postgres backup options in 2026, when pg_dump is enough, how WAL archiving and point-in-time recovery work, how pgBackRest, WAL-G and Barman compare, and what RDS and Cloud SQL do and do not give you.
pg_dump vs physical backups: which one is a real backup?
There are two families of PostgreSQL backup, and they answer different questions.
A logical backup with pg_dump reads the database through SQL and writes out the schema and rows. The docs are clear that it "makes consistent exports even if the database is being used concurrently", because it runs inside a single snapshot. The output is portable across major versions and architectures, you can restore one table out of it, and PostgreSQL 18 added --statistics and related options so planner statistics can travel with the dump. What it cannot do is recover to 14:32 on a Tuesday. A dump is a single point in time, and your recovery point objective is however long ago the last one finished. On a large database the restore is also slow, because every index gets rebuilt from scratch.
A physical backup copies the data directory files themselves, either with pg_basebackup or a dedicated tool. On its own it is also just a point in time. Its value comes from pairing it with the write-ahead log (WAL): every change PostgreSQL makes is written to WAL first, so if you keep a base backup plus every WAL segment produced since, you can replay forward to any moment in between. That is point-in-time recovery (PITR), and it is the thing you actually want when someone runs a DELETE without a WHERE.
My rule is simple. Use physical backups plus WAL archiving for anything you would be paged about. Keep pg_dump for migrations, major-version moves, seeding test environments and the occasional "give me last week's copy of one table". If you want the wider picture of RPO, RTO and the 3-2-1 rule, I covered that in Backup and Disaster Recovery in 2026.
How WAL archiving and point-in-time recovery work
PostgreSQL writes WAL in 16 MB segment files. With archive_mode = on, every time a segment is completed the server runs archive_command (or an archive_library) and only recycles the segment once that command returns success. If the command keeps failing, segments pile up in pg_wal until the disk fills. That failure mode is the single most common way Postgres backups go wrong, and it is worth an alert of its own.
Two settings shape your real recovery point. The archiver only fires on completed segments, so a quiet database can sit on an unarchived segment for a long time; archive_timeout forces a switch, and the docs suggest "a minute or so" as a reasonable value. And the recovery side is controlled by restore_command plus the recovery_target_* parameters: recovery_target_time picks the moment, recovery_target_action decides what happens when it is reached (the default is pause), and recovery_target_timeline defaults to latest.
Restoring is "lay down the newest base backup taken before the target, then replay archived WAL up to the target". The tools below exist mostly to make the base backups cheaper, the WAL shipping parallel and compressed, and the restore a single command.
PostgreSQL 17 incremental backups: pg_basebackup --incremental and pg_combinebackup
Until PostgreSQL 17, every pg_basebackup was a full copy. Version 17 added pg_basebackup --incremental=old_manifest_file, which sends only blocks changed since a reference backup. To know which blocks changed, the server needs the WAL summarizer running: set summarize_wal = on (default off) before the full backup you want to build on.
The catch is in the docs: "An incremental backup cannot be used directly". You must first run pg_combinebackup to merge the chain into a synthetic full backup. PostgreSQL 18 added -k/--link to that tool so the merge can use hard links instead of copies, alongside the existing --clone and --copy-file-range modes.
# PostgreSQL 18.6: native full + incremental chain, no third-party tool
# postgresql.conf must already have: summarize_wal = on, wal_level = replica
# 1. Full base backup with a manifest (manifests are on by default)
pg_basebackup -D /backups/full-2026-11-09 -X stream --checkpoint=fast
# 2. Next day: incremental against the full backup's manifest
pg_basebackup -D /backups/incr-2026-11-10 -X stream \
--incremental=/backups/full-2026-11-09/backup_manifest
# 3. At restore time: combine the chain (oldest first) into a usable data dir
pg_combinebackup /backups/full-2026-11-09 /backups/incr-2026-11-10 \
-o /var/lib/postgresql/18/restore --clone # reflinks where the filesystem supports them
# 4. Verify the result before you trust it
pg_verifybackup /backups/full-2026-11-09
This is real progress, and for a small single-server setup it removes the need for extra software. What it does not give you is WAL archiving to object storage, retention, encryption, parallelism or a catalogue of what you have. Those are exactly what the dedicated tools add.
pgBackRest vs WAL-G vs Barman: the 2026 comparison
All three do physical backups plus continuous WAL archiving to disk or object storage, and all three restore to a point in time. They differ in philosophy and in who keeps them alive.
| pgBackRest | WAL-G | Barman | |
|---|---|---|---|
| Latest release | 2.60.0, 18 Oct 2026 | v3.0.9, 20 Aug 2026 | 3.20.1, 29 Sep 2026 |
| Written in | C | Go | Python (3.12+ since 3.20.0) |
| Databases | PostgreSQL only | PostgreSQL, MySQL/MariaDB, MongoDB, Redis, Greenplum and more | PostgreSQL only |
| Backup types | full, diff, incr, plus block incremental | full plus delta chains (WALG_DELTA_MAX_STEPS, default 0) |
full, rsync file-level incremental, PG17 block-level via barman backup --incremental |
| Storage | POSIX, S3, Azure, GCS, SFTP | S3, GCS, Azure, Swift, Alibaba OSS, SSH, file system | Barman server disk, plus barman-cloud-* tools for S3, Azure, GCS |
| Encryption | client-side aes-256-cbc |
libsodium or PGP keys | S3 SSE-C added in 3.20.0 |
| Shape | config file plus archive-push / archive-get |
env vars, single binary | dedicated backup server managing many Postgres hosts |
pgBackRest is what I reach for on self-managed Postgres. The 2.60.0 release added a checksum-page-error option, and with it the restore command now stops on any restored file with an invalid page checksum, while expire refuses to expire if the oldest retained backup would contain checksum errors. That will surprise people who upgrade blind, which is the point. It also gained GCS Workload Identity Federation and object-store read prefetch. Two earlier changes matter too: since 2.59.0 only restore may run as root by default, and 2.59.3 fixed a bug where encryption subkeys and salts could be generated weakly if OpenSSL could not read system randomness. The news page notes the archive subkeys are created once by stanza-create, so upgrading does not replace a weak one. The project lists sponsors including AWS, Supabase, Percona and Dalibo, which is a better sign of longevity than a single vendor.
WAL-G is a single Go binary configured with environment variables, and it is the natural pick when you also back up MySQL or MongoDB and want one tool. Its 3.0.9 release was mostly CI, dependency bumps and Greenplum/Cloudberry work, plus a timeline switch check for Postgres. Delta backups are off unless you set WALG_DELTA_MAX_STEPS.
Barman, maintained by EnterpriseDB, is built around a central backup server that pulls from many Postgres instances. It supported PG17 block-level incremental backups from 3.11.0, requiring backup_method = postgres and summarize_wal. The 3.20.1 release fixed CVE-2026-93853, where anyone able to overwrite the backup.info catalogue in the bucket could make Barman delete unrelated cloud snapshots with its own credentials. If you run Barman with snapshot backups, upgrade.
What RDS and Cloud SQL backups actually cover
Managed databases make backups somebody else's job, within limits you should know before the bad day.
On Amazon RDS, automated backup retention can be set from 0 to 35 days; the console defaults to seven, the API to one. Point-in-time restore always creates a new DB instance, and RDS uploads transaction logs to S3 every five minutes, so your latest restorable time trails by roughly that much. You get a new endpoint, new parameter-group attachment questions and DNS to repoint.
On Cloud SQL for PostgreSQL, the PITR log retention window is 1 to 7 days on Enterprise edition and 1 to 35 days on Enterprise Plus, where the default is 14. Standard backups can be retained from 1 to 365 days, and the newer enhanced backups go up to 10 years. Those long-lived backups are snapshots, not PITR: you can return to the day, not the minute.
The other limit is that a managed backup lives in the same account and usually the same provider as the database. If an attacker or a bad Terraform run deletes the instance, you are relying on the provider's deleted-instance retention. Cross-account or cross-region copies are the managed-world version of the "1 offsite" in 3-2-1, and they are opt-in.
How to test Postgres restores (the part everyone skips)
A backup system has three outputs: backups, alerts, and restores. Most teams monitor the first, some the second, almost nobody the third. What I would put in place, whatever tool you pick:
- A scheduled restore drill. Weekly, restore the latest backup to a throwaway host, replay WAL to a recent timestamp, start Postgres, and run a few queries that prove the data is current (
select max(created_at) from orders). Record how long it took. That number is your real RTO. - Archive monitoring. Alert on
pg_stat_archiver.failed_countincreasing and onlast_archived_timegoing stale. A silently failingarchive_commandis how teams discover their PITR window is zero. - Tool-level verification.
pgbackrest verify,pg_verifybackupandbarman checkcatch missing or corrupt files before you need them. - Page checksums. Enable data checksums so tools can detect corruption at backup time rather than restore time. The pgBackRest 2.60 change above only helps if checksums exist.
If you run Postgres on Kubernetes, CloudNativePG wraps much of this (base backups and WAL archiving to object storage) into the operator, and the same drill applies. And if your schema changes are where the risk lives, zero-downtime migrations reduce how often you need PITR at all.
Which Postgres backup tool should you choose?
- One or two self-managed servers, all Postgres: pgBackRest to S3, full weekly, differential daily, block incremental on, async archiving. The step-by-step PITR guide sets this up end to end.
- Mixed fleet with MySQL or MongoDB too: WAL-G, so one binary and one set of habits covers all of it.
- Many Postgres hosts and a team that wants a central backup server: Barman, especially if you already buy support from EnterpriseDB.
- Tiny database, no PITR requirement: native
pg_basebackupwith incremental chains, or honestlypg_dumpto versioned object storage. - RDS or Cloud SQL: use the managed backups, raise retention to the maximum your edition allows, and add a cross-account copy plus a periodic
pg_dumpfor the restore you cannot do inside the provider.
FAQ
Is pg_dump enough for backing up PostgreSQL?
For small databases where losing up to a day of data is acceptable, yes. It cannot do point-in-time recovery, and restoring large databases is slow because indexes are rebuilt. For production systems, pair physical backups with WAL archiving and keep pg_dump for migrations and single-table recovery.
Is pgBackRest better than WAL-G?
For PostgreSQL-only estates, pgBackRest has the richer feature set: differential and block incremental backups, multiple repositories, delta restore and built-in verification. WAL-G is simpler to deploy as one Go binary and covers several database engines. Both restore to a point in time from S3.
Do I still need pgBackRest now that PostgreSQL 17 has incremental backups?
Native incremental backups remove the full-copy cost, but you still have to build WAL archiving, retention, encryption, object storage upload and verification yourself. pgBackRest, WAL-G and Barman provide those. For a single small server, native tools can be enough.
How far back can I restore an RDS PostgreSQL database?
Up to the automated backup retention period, which can be set from 0 to 35 days. The restore creates a new DB instance, and the latest restorable time is typically about five minutes behind because transaction logs are uploaded to S3 every five minutes.
This quarter I would do three things on any Postgres I am responsible for: upgrade pgBackRest to 2.60 in staging first and read the checksum warnings it produces, add an alert on pg_stat_archiver failures, and schedule an automated restore drill that posts its duration somewhere a human looks. PostgreSQL 14 also reaches end of life on 12 November 2026, and the PostgreSQL 19 release candidate is already out, so if you are planning the major upgrade, take a fresh full backup and a pg_dump the day before. Restores are the only part of a backup system anyone remembers.
Related: Backup and Disaster Recovery in 2026: RPO vs RTO, 3-2-1-1-0, Real Costs, How to Run Postgres on Kubernetes with CloudNativePG 1.30 (2026) and Should You Run Databases on Kubernetes in 2026? 66% Do, RDS Costs 1.7x.
Spot an error or something out of date? Email me and I will fix it and log it on the corrections page.
I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.
Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.
Related
Is Ansible Still Relevant in 2026? ansible-core 2.22 vs Terraform
Is Ansible still relevant in 2026? What ansible-core 2.22 and Ansible 14 changed, how Ansible compares with Terraform, cloud-init and Kubernetes, where configuration management still wins, and what Event-Driven Ansible and Lightspeed actually do.
11 minNov 10, 2026How to Fix AWS ALB Unhealthy Targets and 502/503 Errors (2026)
Fix AWS Application Load Balancer unhealthy targets and 502, 503 and 504 errors step by step: read the health reason code, fix health check paths and success codes, security groups, ip vs instance targets, EKS readiness gates, deregistration delay, keep-alive timeouts and access logs.
13 minNov 10, 2026How to Fix a systemd Service That Fails to Start (2026 Guide)
Step-by-step fixes for a failed systemd service: read systemctl status and journalctl -u, decode exit codes 203/EXEC, 217/USER and 226/NAMESPACE, stop restart loops and start-limit-hit, fix environment files and permissions, and write a correct unit file.
11 min