A backup that has never been restored is not a verified backup — it's an assumption. This guide covers building a regular verification process so you discover a broken backup during a routine test, not during an actual emergency.
Why This Step Is Frequently Skipped (And Why It Matters)
Backup creation is easy to automate and forget about. Verification requires deliberate effort, so it's the step most commonly neglected — and the one most likely to reveal a problem (corrupted file, incomplete dump, wrong credentials) that would otherwise only surface during a real disaster.
Level 1 — Confirm the Backup File Exists and Has Reasonable Size
ls -lh /var/backups/
A database dump that's suspiciously small (a few KB when it should be megabytes) indicates a failed or empty backup, not a successful one.
Level 2 — Confirm the File Isn't Corrupted
gunzip -t backup.sql.gz
Tests the gzip integrity without actually extracting — a quick sanity check.
Level 3 — Restore into a Temporary Database
mysql -u root -p -e "CREATE DATABASE restore_test;"
gunzip -c backup.sql.gz | mysql -u root -p restore_test
mysql -u root -p -e "SHOW TABLES;" restore_test
Confirm expected tables exist and contain data:
mysql -u root -p -e "SELECT COUNT(*) FROM restore_test.orders;"
Clean up afterward:
mysql -u root -p -e "DROP DATABASE restore_test;"
Level 4 — Full Restore on a Separate Test Server
The most thorough test: spin up a temporary VPS, restore your complete backup (database and files) onto it, and confirm the application actually runs correctly — this catches issues a database-only test misses, like missing configuration files or broken file permissions.
Automating Verification
sudo nano /usr/local/bin/verify-backup.sh
#!/bin/bash
LATEST_BACKUP=$(ls -t /var/backups/*.sql.gz | head -1)
gunzip -t "$LATEST_BACKUP"
if [ $? -ne 0 ]; then
echo "Backup integrity check FAILED: $LATEST_BACKUP" | mail -s "ALERT: Backup Verification Failed" [email protected]
exit 1
fi
mysql -u root -e "DROP DATABASE IF EXISTS restore_test; CREATE DATABASE restore_test;"
gunzip -c "$LATEST_BACKUP" | mysql -u root restore_test
TABLE_COUNT=$(mysql -u root -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='restore_test';")
if [ "$TABLE_COUNT" -eq 0 ]; then
echo "Restore verification FAILED: no tables found" | mail -s "ALERT: Backup Verification Failed" [email protected]
fi
mysql -u root -e "DROP DATABASE restore_test;"
Scheduling Verification
sudo crontab -e
0 4 * * * /usr/local/bin/verify-backup.sh
Run this after your backup job completes, so it's always checking the most recent backup.
How Often Should You Test?
| Verification Level | Suggested Frequency |
|---|---|
| Automated integrity check | Every backup run (daily) |
| Restore into test database | Weekly |
| Full separate-server restore test | Quarterly, or before major system changes |
Documenting Your Recovery Process
Write down the exact restore procedure (commands, credentials location, expected time to complete) while things are calm — not while trying to remember it during an actual outage.
Common Discoveries During Verification
- Backup script had been silently failing for weeks due to a credential change
- Disk filled up, truncating recent backups
- Off-site sync stopped working after a firewall change
Related Articles
- Backup Strategy 101: The 3-2-1 Rule Explained
- How to Set Up Automated VPS Backups (rsync, cron & Off-Site Storage)
- How to Create a Disaster Recovery Plan for Your VPS
