Analyses / Backup evidence
Backup verification is not a restore test
13 October 2026 · Evidence review · PostgreSQL 18
A clean file check is not evidence that an application has been recovered. Record integrity checks, backup dependencies and restoration results separately.
Three questions, three kinds of evidence
NIST distinguishes testing backup reliability and integrity from restoring selected system functions using a sample. Neither step is a substitute for the other. [8, CP-9(1)-(2)]
Comparison of documented checks, not experimental results.
| Question | Evidence to retain | Still unproven |
|---|---|---|
| Are the files intact? | Validator, manifest, selected checks and errors | Application recovery and correct business data |
| Are dependencies complete? | Required predecessors and recovery-log coverage | Integrity of every input and successful replay |
| Does recovery work? | Restored scope and functional acceptance results | Untested backups, environments and failure scenarios |
What PostgreSQL checks
PostgreSQL 18's pg_verifybackup compares a base backup with its manifest and can parse required write-ahead log (WAL) records. Extra WAL outside that requirement is not checked. WAL parsing needs matching-version tools and plain-format backups; tar verification requires --no-parse-wal. Record exclusions, including --skip-checksums. None of this executes recovery. [1]
The check deliberately excludes postgresql.auto.conf,standby.signal and recovery.signal. A passing result therefore does not attest to the intended recovery configuration. [1]
The manifest's WAL ranges specify a timeline and replay boundaries. This implies that their minimum end position is not proof that the archive reaches a later recovery target. [6] A point-in-time recovery also needs the appropriate continuous WAL sequence. [3]
A manifest is evidence, not an independent witness
PostgreSQL's manifest is a UTF-8 JSON document. [4] File entries carry paths, sizes and optional checksums; modification timestamps are informational. [5]
The default per-file CRC32C detects accidental changes but is not designed to resist malicious modification. Selecting SHA digests does not help if an attacker can rewrite both files and their expected hashes: the manifest needs a separate trust boundary. [7] Our practical inference: record where the manifest came from and how it was protected, not only whether hashes matched.
The newest incremental backup may not stand alone
An incremental backup refers to an earlier backup, not necessarily the immediately preceding one. PostgreSQL needs all predecessors supplying omitted blocks, plus the recovery WAL. PostgreSQL has no built-in mechanism to track which earlier backups remain necessary for later incremental restores. [3, section 25.3.3]
pg_combinebackup reconstructs a full copy and checks the supplied chain's relationships, not each input's integrity. Verify inputs separately. Its --link option creates hard links: starting a server on the output can change files shared with the inputs. Do not treat that output as an independent disposable restore copy. [2]
Retention protects a copy; it does not test it
S3 Object Lock protects individual versions; new versions and delete markers can still be created. [9] Hesela's inference is straightforward: retaining the wrong version, or an incomplete dependency set, does not make the required recovery possible. Keep version identity and dependency evidence alongside retention settings.
A retained version can already be compromised
Strunk and colleagues' OSDI 2000 S4 prototype separated storage protection from a potentially compromised host. Its version history and audit records covered a bounded detection window; the newest backup could already include harmful changes.[10, sections 2.2 and 3.3]
This is historical design evidence, not a test of modern Object Lock. Hesela's inference: a restore acceptance record should explain why its recovery point is trusted, not merely confirm that retained bytes match their hashes.
A useful restore test has acceptance criteria
NIST's sampled restoration checks selected functions; broader recovery returns a system to a known state within defined objectives. [8, CP-9(2), CP-10] For PostgreSQL, inspect the recovered database's contents against the desired state before opening access. [3, section 25.3.5]
Hesela's recommended test record, not a vendor-mandated checklist:
- Identify the backup, object versions, predecessor set and intended recovery point.
- State why that recovery point is acceptable, including any known compromise window.
- Record tool versions, options, exclusions and integrity-check results.
- Use an isolated recovery destination; preserve the source backups.
- Record restored scope, data checks, application checks and observed failures.
- Measure elapsed time with explicit start and end events; distinguish retrieval, replay and acceptance time.
- State what was sampled, what was not tested and how the test environment differs.
This article reviews documentation. No restore experiment was performed, no recovery duration was measured, and no probability of recovery is estimated.
Definitions: backup verification, backup manifest, incremental backup, restore test and storage immutability.
Related: checksum and identity boundaries, WAL and checkpoint boundaries and what disk scrubbing establishes.
Primary sources and scope
- PostgreSQL 18: pg_verifybackup, checks and exclusions
- PostgreSQL 18: pg_combinebackup, dependency chains and hard links
- PostgreSQL 18: Continuous Archiving and Point-in-Time Recovery, sections 25.3.3 and 25.3.5
- PostgreSQL 18: Backup Manifest Format
- PostgreSQL 18: Backup Manifest File Object
- PostgreSQL 18: Backup Manifest WAL Range Object
- PostgreSQL 18: pg_basebackup, manifest checksums and trust
- NIST SP 800-53 Rev. 5: CP-9(1), CP-9(2) and CP-10
- Amazon S3 User Guide: Locking objects with Object Lock
- Strunk et al.: Self-Securing Storage: Protecting Data in Compromised Systems, OSDI 2000, sections 2.2, 3.3 and 5
Sources reviewed 13 October 2026. PostgreSQL examples are pinned to version 18. NIST is used for the distinction between control types, not as a claim of certification or universal legal obligation.