A backup that has never been tested is not a backup — it's a hope. Three commands, one drill, and a monitoring loop turn that hope into something you can actually depend on.
People talk about “testing backups” like it's a single thing. It's three:
restic check covers this.restic check --read-data downloads everything and recomputes checksums.Skip any of these and you have an unverified backup. Most people stop at the first one and find out about the gap during a real outage.
This is fast and cheap. Runs in seconds to a few minutes depending on repository size. Should be the last step of every backup script.
restic check
What it does: verifies all pack files are reachable, the index matches the data, and snapshot trees don't reference missing blobs. What it does NOT do: read the actual encrypted data. A pack file that's corrupted on disk but still has the right size and metadata will pass.
Wire it into your backup script:
#!/usr/bin/env bash
set -euo pipefail
source /etc/restic/env
restic backup /home /etc /var/lib
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check
If restic check fails, the next step is to investigate, not to keep backing up. A structurally-broken repo will silently lose data on the next prune.
This is the expensive one. restic check --read-data downloads every encrypted blob in the repository and recomputes its checksum. It catches bit rot, storage corruption, and any tampering at the storage layer that didn't also touch the metadata.
restic check --read-data
On a 100 GB repository over a 100 Mbps connection, expect ~2 hours. On 1 TB at the same bandwidth, ~20 hours. Plan accordingly.
For metered storage providers (B2, S3, rsync.net) this gets expensive. Most people compromise with --read-data-subset, which verifies a percentage of randomly-sampled packs:
# Verify 10% of packs every week, full verify quarterly
restic check --read-data-subset 10%
ServerCrate has no egress fees, so --read-data runs free regardless of repository size. Schedule it monthly on Saturday nights.
This is the only verification that proves your backups will actually save you. Pick a known-good file, restore it to a scratch directory, diff it against the original.
# Pick a stable file you know hasn't changed
TARGET="/etc/hostname"
# Get the latest snapshot ID
SNAP=$(restic snapshots --json | jq -r '.[-1].id')
# Restore to a temp dir
mkdir -p /tmp/restic-drill
restic restore "$SNAP" --target /tmp/restic-drill --include "$TARGET"
# Compare
diff "$TARGET" "/tmp/restic-drill$TARGET" && echo "PASS" || echo "FAIL"
# Clean up
rm -rf /tmp/restic-drill
Run this quarterly minimum. Annually for sure. Pick a different file each time so you're not just restoring the same byte pattern over and over.
For the more paranoid: also restore from the oldest snapshot in the repo, not just the newest. A repository that can restore the latest snapshot but not the oldest one has a deeper integrity problem worth catching.
The verification is only useful if someone notices when it fails. The pattern that works:
curl -fsS --retry 3 https://hc-ping.com/<uuid> on success. Healthchecks.io alerts if a ping doesn't arrive within the expected window.check and still be unrestorable due to storage-layer corruption.ServerCrate is a vanilla Restic backend — everything above works exactly as documented. Three things that help specifically:
restic check --read-data downloads the entire repository every time you run it. On B2 or S3 that costs real money. On ServerCrate it's free, so you can run full verification as often as you want without watching the meter.--read-data runs are unlikely to find storage-side problems — they'll only catch issues in transit or in your local restic environment.Related guides: restic forget retention policy, restic restore commands, automating restic with systemd.
10 GB free, no card. Run restic check --read-data as often as you want.
Cancel anytime. 10 GB free tier never expires. No egress fees.