Recent development · Practical technical guide · 20 October 2026
pgBackRest 2.60.0, released on 18 October 2026, makes restore stop when a restored file has an invalid PostgreSQL page checksum and makes expire refuse when the oldest retained backup has checksum errors. What changed, why, and how to adjust restore runbooks and retention monitoring.
A default that changes what a restore means
pgBackRest 2.60.0 was released on 18 October 2026 with an important note at the top of its release text: the restore command will now error and stop if any restored file contains an invalid page checksum, which leaves the cluster incomplete and unable to start, so the restore must be rerun; and the expire command will now refuse to expire if the oldest retained backup would contain checksum errors, which also prevents archive from being expired in that repository. Both behaviours come from one new option, checksum-page-error, and both can be turned back into warnings with checksum-page-error=n.
For most teams the right response is to keep the new defaults and change the runbook around them. A restore that stops on corruption is better than a restore that starts a database with corrupted pages and reports success. But a restore is often run under pressure, and a new way for it to fail deserves rehearsal before the day it happens.
What the option does, command by command
The commit that added the option, d7f54b2 of 15 September 2026, describes its behaviour for each command. pgBackRest already validated PostgreSQL data page checksums during backup when the cluster had checksums enabled; the configuration reference explains that failures did not abort the backup but produced warnings and a list of invalid pages stored in the backup manifest.
| Command | Default for checksum-page-error | Behaviour when enabled | Behaviour when disabled |
|---|---|---|---|
| backup | n (unchanged behaviour) | Terminates with an error as soon as an invalid page checksum is found | Warns per file, and now also prints a total at the end |
| restore | y | Terminates with an error as soon as a file with invalid page checksums is restored, before pg_control is renamed, so the cluster cannot start | Warns per file and reports the total; previously nothing was reported |
| expire | y | If the backup that would become the oldest retained one has invalid checksums, errors before removing anything | Proceeds and warns; an ad hoc expire proceeds in either case with the warning |
The restore row contains the most significant detail. Earlier versions did not report invalid page checksums during restore at all; a backup taken while pages were already corrupt restored silently. The new behaviour stops before pg_control is renamed, which is the step that makes the restored directory a startable cluster, so a half-restored directory cannot be started by accident. Page misalignment, a file whose size is not a whole number of pages, is treated the same way during backup because it prevents checksum validation.
Why the backup default stayed lenient
A backup that aborts on the first bad page would leave you with no new backup at all on a cluster that already has a corrupted block, which is the cluster that most needs one. Keeping backup at warn, while making the warning harder to miss with an end-of-run total, preserves the ability to back up a damaged cluster. The strictness moves to the two points where a silent failure is most expensive: when you restore, and when retention is about to delete older, possibly clean, backups in favour of a damaged one.
The expire rule is the subtle one. Retention normally deletes the oldest backups once newer ones exist. If the backup that would become the oldest retained one contains checksum errors, deleting everything older would leave a damaged backup as the earliest restore point you have. Refusing keeps the older, possibly clean, backups and the archive that goes with them. The cost is that the repository grows until someone deals with it, and the WAL archive for that repository stops being expired.
Worked example: the restore rehearsal that fails
This scenario is illustrative. A team runs a monthly restore test of its production stanza into a scratch host. After upgrading to 2.60.0 the test fails part-way through with an error naming a relation file with invalid page checksums. The cluster directory is incomplete and will not start, which is the documented behaviour.
The backup logs explain it: every nightly backup for the past nine days had warned about the same file, and nobody read the warnings. The team now has three decisions, and the new behaviour forces them to be made deliberately rather than discovered later. First, find the cause on the primary, starting with the PostgreSQL checksum documentation and an offline pg_checksums --check on a copy, or the relation that owns the file. Second, decide which restore point to use: the expire refusal has kept backups from before the corruption, so a restore from ten days ago plus WAL replay may produce a clean cluster. Third, if they must restore the damaged backup to recover what they can, rerun the restore with --checksum-page-error=n for that single run, and read the reported list of damaged files.
Illustrative commands; paths and stanza name are placeholders. Check the 2.60.0 command reference for your configuration.
# Rehearsal: restore the latest backup into a scratch directory with the new default
pgbackrest --stanza=main --pg1-path=/restore/test restore
# Only for a deliberate salvage of a known-damaged backup, for this run only
pgbackrest --stanza=main --pg1-path=/restore/test --delta --checksum-page-error=n restore
# Retention view after an expire refusal: which backup carries checksum errors
pgbackrest --stanza=main infoChanging the runbook and the monitoring
- Upgrade a non-production repository first and run a full restore rehearsal, so the first stop on corruption happens in a test.
- Alert on the end-of-backup checksum warning total, which 2.60.0 now prints, instead of relying on someone reading per-file warnings.
- Alert on expire failures and on repository growth; an expire refusal means retention has stopped and the WAL archive is accumulating.
- Write the salvage procedure into the restore runbook: when to rerun with checksum-page-error=n, who approves it, and how to record which files were damaged.
- Confirm that data checksums are enabled on your clusters; without them PostgreSQL computes no page checksums and none of this validation can happen.
What else is in 2.60.0
The release also adds GCS Workload Identity Federation, prefetching of multi-file and range reads from object stores, fewer keep-alive requests to remote processes, retries for Azure batch sub-requests missing from responses, OpenSSL 4 support, and a migration of code generation, documentation tools and the test harness to Python. None of those changes behaviour the way the checksum option does, but the object store prefetch is worth a restore-time comparison if your repository lives in S3, GCS or Azure.
Failure modes
- Upgrading during an incident and meeting the new restore stop for the first time under pressure.
- Setting checksum-page-error=n globally to make a restore succeed, which removes the protection for every later restore.
- Ignoring a stuck expire until the repository or archive volume fills.
- Running clusters without data checksums, so neither backup nor restore can detect corrupted pages.
- Treating a restore that starts as a restore that succeeded; verify the data with application checks as well.
Sources and scope
Checked against these sources on 20 October 2026. Event: pgBackRest 2.60.0 release, 18 October 2026; recheck it before acting. Examples and figures are illustrative, not measurements.
Rehearse a restore on 2.60.0 before production, alert on checksum warning totals and expire failures, and write when a one-off checksum-page-error=n salvage restore is allowed.