Zero-dependency Python tool that verifies the integrity of backup archives.
Knows about tar, tar.gz, tar.bz2, tar.xz, and zip. Checks readability, validates archive structure, optionally compares against SHA256 checksums, and can extract into a temp directory to confirm a backup is restorable.
Perfect for cron jobs that run after your backup script so you get a heads up before you actually need to restore from something that is broken.
You rotate backups. You copy them to offsite storage. You feel safe.
But have you ever tried to restore one of those archives three months later only to find it was truncated or corrupt the whole time? It happens more than anyone wants to admit. This tool catches that problem early.
- Validates gzip CRC, zip CRC32, tar structure, bzip2 stream, and xz headers
- Detects truncated and zero-byte files that other tools silently accept
- Optional SHA256 comparison against a checksum file (shasum -c format or CSV)
- Optional
--extracttest that unpacks into a temp directory to confirm restorability - Directory scanning automatically picks up common backup file extensions
- Glob patterns work too (
/backups/nightly-*.tar.gz) - JSON output mode for wiring into monitoring systems
- Cron-friendly exit codes (see below)
- Pure Python 3.8+ standard library. No pip. No venv. No dependencies.
- Python 3.8 or newer
- Nothing else. Uses only the standard library.
Copy the single script wherever you like and make it executable:
curl -O https://raw.githubusercontent.com/cappy-dev/backup-verify/main/backup_verify.py
chmod +x backup_verify.pyOr just clone the repo:
git clone https://github.com/cappy-dev/backup-verify.git
cd backup-verifypython3 backup_verify.py /backupsOutput (colorized when run in a terminal):
PASS nightly.tar.gz (2048.0 KB)
+ readable: ok
+ archive: gzip CRC ok
FAIL weekly.tar.gz (50.0 KB)
+ readable: ok
+ archive: gzip error: Compressed file ended before the end-of-stream marker was reached
FAIL 1 passed, 1 failed of 2 total
python3 backup_verify.py /backups/nightly.tar.gzpython3 backup_verify.py "/backups/2026-*.tar.gz"Generate checksums alongside your backups:
cd /backups
sha256sum *.tar.gz > checksums.txtThen verify:
python3 backup_verify.py /backups --checksums /backups/checksums.txtThe checksum file uses the standard shasum format (compatible with sha256sum -c).
A CSV file with hash,filename columns also works as a fallback.
The --extract flag unpacks each archive into a temporary directory,
confirms the extraction succeeds, and reports how many bytes came out:
python3 backup_verify.py /backups --extractpython3 backup_verify.py /backups --json{
"version": "1.0.0",
"total": 2,
"passed": 1,
"failed": 1,
"results": [ ... ]
}Pipe it to jq for quick checks:
python3 backup_verify.py /backups --json | jq '.failed'These are designed to work well with cron and monitoring systems:
| Code | Meaning |
|---|---|
| 0 | Every backup passed verification |
| 1 | At least one backup failed |
| 2 | Argument or configuration error |
| 3 | No backups matched the given path |
Run verification one hour after your nightly backup:
# /etc/cron.d/backup-verify
0 4 * * * root /opt/backup-verify/backup_verify.py /backups --json >> /var/log/backup-verify.log 2>&1A non-zero exit code means something needs your attention. Wire it into your existing alert system (webhook, email, monitoring agent) based on the exit code.
| Extension | Format | Validation |
|---|---|---|
| .tar | uncompressed | tar member iteration |
| .tar.gz | gzip | gzip CRC check + tar members |
| .tgz | gzip | same as above |
| .tar.bz2 | bzip2 | stream decompression + tar |
| .tbz2 | bzip2 | same as above |
| .tar.xz | xz | tar member iteration |
| .txz | xz | same as above |
| .zip | zip | CRC32 via testzip() |
| .gz | gzip | gzip CRC check (single file) |
| .bz2 | bzip2 | stream decompression |
| .xz | xz | header check |
When pointed at a directory, all of the above extensions are auto-detected. When pointed at a single file path, the file is checked regardless of extension.
MIT. See LICENSE.