HomeTechOps

Self-Hosting

A home-server backup you've actually restored

Prove your backup recovers, don't assume it — 3-2-1 extended to 3-2-1-1-0 (one immutable copy, zero errors after a tested restore), application-consistent database dumps, restic check, and a real restore drill to scratch.

Problem summary

Most home-server operators conflate 'the backup job ran' with 'I have a recoverable backup' — different claims. A job can succeed for months while producing a non-restorable archive: a torn database write, a forgotten volume, bit rot, an expired key. The operator-grade answer extends 3-2-1 to 3-2-1-1-0 — one copy that's immutable/offline/air-gapped, and zero errors after a verified restore test. The two disciplines that make it real: application-consistent database backups (stop the container or dump with `pg_dump`/`mysqldump`/SQLite `.backup` — never `cp` a hot DB file), and a restore drill that recovers to a scratch location and proves the app boots, not just that bytes copied.

Operator snapshotEvidence first
First proof

Count your copies, media types, and off-site location.

Screen to open

restic check

Expected signal

At least 3 copies, on 2 media, with 1 off-site.

Stop boundary

If the app won't boot on restored data, the backup isn't done.

Layer path

1'The backup job ran' and 'I have a recoverable backup' are different claims. A job can succeed for months while producing a non-restorable archive — a torn DB write, a forgotten volume, bit rot, an expired key.
23-2-1 (3 copies, 2 media, 1 off-site) answers how-many-and-where but nothing about ransomware or recoverability. 3-2-1-1-0 adds one immutable/offline/air-gapped copy and zero errors after a verified restore test.
3Databases need application-consistent backups: copying a live SQLite/Postgres/MySQL file mid-write captures torn pages that may not reopen. Stop the container or take a logical dump, then back up the dump.
4The discipline that makes 3-2-1-1-0 real is the restore drill: recover to a scratch location, verify checksums, and boot the app against the restored data — service-level proof, not a file copy.
Runbook

Step-by-step runbook

Start here. Do each check in order, compare it to the expected result, and stop when the evidence explains the failure or the safe stop point applies.

1

Reach 3-2-1

Check: Ensure 3 copies, 2 media, 1 off-site.

Expected result: A single event can't destroy everything.

If not: Add whatever copy/media/location is missing.

2

Add the immutable '1'

Check: Make one copy object-locked, append-only, or offline.

Expected result: Ransomware/delete can't reach every copy.

If not: Object-lock a bucket or keep an unplugged drive.

3

Make DB backups consistent

Check: Dump databases or back them up while stopped.

Expected result: DB backups reopen cleanly.

If not: Stop copying live DB files.

4

Schedule integrity checks

Check: Run the "Check repository structure" command below regularly and --read-data periodically.

Expected result: Corruption is caught early.

If not: Bound --read-data with a subset on large repos.

5

Run a restore drill

Check: Restore to scratch, verify checksums, boot the app.

Expected result: Recovery is proven end-to-end (the '0').

If not: Never restore over live data during a drill.

Safe stop: If the app won't boot on restored data, the backup isn't done.

6

Escrow the key

Check: Store the repo key/passphrase separately and test access.

Expected result: You can actually decrypt in an emergency.

If not: A lost key makes the backup worthless.

Decision tree

Decision tree

If: All copies are online/mutable

Then: No ransomware/accidental-delete resistance.

Action: Add an immutable (object-lock/append-only) or offline copy.

Safe stop: Don't trust a backup set that one compromise can erase.

If: Databases are copied as live files

Then: Backups may be torn/inconsistent.

Action: Dump the DB or stop the container before backing up its files.

If: Repo never integrity-checked

Then: Silent corruption is possible.

Action: Run restic check regularly; --read-data periodically on a schedule you can afford.

If: Never restore-tested

Then: Recoverability is unproven.

Action: Restore to scratch, verify checksums, and boot the app.

Safe stop: Treat 'never restored' as 'no backup' for critical data.

If: Restore needs a key you don't have

Then: Encryption key not escrowed.

Action: Back up the key/passphrase separately and test access.

Evidence

Evidence table

SymptomEvidence to collectLikely layerNext action
Ransomware/delete wiped 'all' backupsWhether any copy was immutable/offlineNo air-gapped/immutable copyAdd object-lock or an offline copy (the '1').
Restored DB won't open / is corruptHow the DB was captured (live copy vs dump)Crash-inconsistent DB backupUse logical dumps or quiesce the container.
Old snapshots fail to readrestic check / --read-data resultsRepo bit rotSchedule integrity checks; keep redundant copies.
Backups exist but nobody has restored oneDate of last restore drillUnproven recoverabilityRun a scratch restore + app boot test.
Can't decrypt the backup in an emergencyWhere the key/passphrase livesKey not escrowedStore the key separately and test it.
Reference

Commands and settings paths

Check repository structure

restic check

Where: On the backup host (or any machine with repo access)

Expected: Reports no errors in the repository structure.

Failure means: Errors indicate metadata/index corruption.

Safe next step: Investigate before relying on the repo; keep a second copy.

Re-hash data to catch bit rot

restic check --read-data-subset=10% # or --read-data for everything

Where: On the backup host

Expected: The sampled (or full) pack data re-hashes cleanly.

Failure means: A hash mismatch means stored data is damaged.

Safe next step: Restore affected data from another copy; rotate media.

Take an application-consistent DB dump

pg_dump -Fc <db> > db.dump # or mysqldump / sqlite3 .backup

Where: On the DB host/container

Expected: A complete logical dump you can restore independently.

Failure means: A partial dump means the DB was unreachable or creds are wrong.

Safe next step: Back up the dump, not the live data file.

Hardware boundary

Hardware and platform boundary

Change only when

  • Add an immutable/offline copy as soon as the data matters (photos, documents, password vault).
  • Schedule periodic --read-data and a calendar'd restore drill once the stack is real.

Evidence that matters

  • 3-2-1-1-0: three copies, two media, one off-site, one immutable/offline, zero errors after a tested restore.
  • Application-consistent database dumps.
  • A restore drill that boots the app on restored data, plus an escrowed key.

Evidence that does not matter

  • Backing up disposable, regenerable artifacts (caches, thumbnails).
  • Raw RAID as a 'backup' — it's availability, not recovery.

Avoid

  • Trusting 'the job ran' as proof of recoverability.
  • Copying live database files.
  • Keeping every copy online and mutable.

Related tool

Use the linked tool to turn this runbook into a guided check for your exact setup.

Backup plan builder

Related problems

Last reviewed

2026-06-03 · Reviewed by HomeTechOps. Built from 2026-06 research verified against Veeam and Backblaze on 3-2-1-1-0, restic's integrity-check docs (check / --read-data / --read-data-subset), and NIST SP 800-34 on testing recovery rather than assuming it. The operator differentiator is the tested-restore '0' and application-consistent DB dumps, not just a green backup job.

Sources/assumptions

  • Assumes a self-hosted stack with one or more databases (Postgres/MySQL/SQLite) plus file data, backed up with a dedup tool (restic/Kopia/Borg are the common choice — stated only as supporting encrypted, deduplicated snapshots).
  • 3-2-1-1-0 framing is from vendor guidance (Veeam/Backblaze) — the extra '1' is the family immutable/offline/air-gapped, whose exact wording varies; the '0' is zero errors after a verified restore test. NIST SP 800-34 grounds the test-don't-assume principle.
  • restic check / --read-data / --read-data-subset are stated from restic's own docs as the integrity mechanism; equivalent verification exists in other tools.

Source-backed checks

HomeTechOps turns official docs and conservative safety rules into a shorter runbook. These links are the source trail for the page direction.