How to Run a Disaster-Recovery Drill (Restore Your Whole Server to a Spare Machine — Before You Have To)

The drill: rebuild the whole server from backups on a spare box, time it, find the gaps. The only test of a backup that counts.

Every backup tutorial in this series ends with "restore one file to prove it". That proves the archive opens. It doesn't prove you can get the server back — every service, every config, every password, in an order that works, in an afternoon rather than a week. A disaster-recovery drill does: once a year, on a spare machine or a VM, you pretend the server is gone and rebuild it from backups only. What you learn fixes the backups; what you write down becomes the runbook.

Quick reference

PhaseWhatTarget time
0. Set upa spare PC, or a VM (81/141) with a blank disk; backups reachable; nothing else15 min
1. Base OSUbuntu install, user, SSH key, network (1–9) or the Ansible playbook (140)20–30 min
2. Secretsrestic/rclone passwords, .env files, API tokens — from the off-server copy10 min
3. Datarestore /etc bits, ~/docker, data folders, database dumps1–3 h
4. Servicescompose up, each stack; proxy; certificates30 min
5. Testa checklist per service, from a client device30 min
6. Scorecardwhat failed, what was missing, how long; fix and re-note20 min

Rules of the drill

Phase 1: base OS

Install Ubuntu on the spare machine (tutorial 1), or spin up a VM. User, key, updates, hostname drill. If you have the Ansible playbook (140): ansible-playbook site.yml --limit drill and most of phases 1, 2 and 4 happen by themselves — the drill is also the test of the playbook.

Without it: tutorials 3–9 by hand, and note how long it took. That's a reason to write the playbook.

Phase 2: secrets

From the password manager or the sheet in the drawer:

Missing any of these ends the drill. Write it on the scorecard; it's the most valuable finding there is.

Phase 3: data

Mount the backup drive, or configure rclone to the cloud copy, or point restic at the REST server (116). Then:

source /root/.restic.env
restic snapshots
restic restore latest --target /tmp/restore --include /etc --include /home/john/docker

From /tmp/restore:

Time this phase per gigabyte; multiply by the real size. If "restore everything" is 30 hours from the cloud, that's a finding: a local copy (23) as well as the off-site one.

Phase 4: services

docker compose up -d in each folder, in dependency order (networks first, then proxy, then apps). Watch docker compose logs for the ones that fail: usually a missing .env, a wrong data path, a database that wants its dump. Nginx: nginx -t. Certificates: a drill box has no public name, so either restore /etc/letsencrypt (it's in the /etc backup) or accept warnings for the test.

Phase 5: test, as a user would

From a laptop, with its hosts file pointing the names at the drill box:

Tick each. Every one that fails is a gap in the backup, the restore order, or the documentation.

Phase 6: the scorecard

In BookStack (88), a page per drill:

ItemResultFix
Total time to "all services up"4 h 10 min—
restic password available off-serverno — on server onlyprinted, in the safe
Nextcloud .envin vault ✓—
Frigate recordingsnot backed up (by choice) ✓—
Vaultwarden restored and unlocked✓—
Immich: 400 GB restore from B2 would take 30 hpartial restore onlyadd local restic repo
Nginx sites for office.example.commissing — created after last /etc snapshot?check backup includes /etc/nginx/sites-available

Fix every "Fix" item this week. Then the backup is known to work, and the runbook is the drill's own notes — the thing you'd follow at 2 am.

How often

Once a year, and after any big change (new storage layout, new backup tool, a migration). A half-drill — restore one stack to a VM — every quarter is cheap and catches most drift.

Troubleshooting the drill

It took all weekend. Normal the first time. The playbook (140), a local restic copy and a written order make the second one an afternoon.

A container starts but the app is empty. Data path mismatch between the compose file and where you restored; docker inspect the mounts (65).

Couldn't test the public services. Hosts-file trick on the laptop, or a temporary DNS record pointing at the drill box's IP for an hour.

Found a gap you can't fix cheaply (the 400 GB). Decide, and write the decision down: "Immich originals also on the backup drive; off-site covers documents only." A known gap is a plan; an unknown one is a disaster.

Wrapping up

A spare box, a Saturday, backups only, secrets from outside the server, every service tested from a laptop, a scorecard with times and fixes. It's the one exercise that turns "I have backups" into "I can be back by tonight" — and the fixes it produces are the most valuable hour you'll spend on the server all year.

Call us