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

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
| Phase | What | Target time |
|---|---|---|
| 0. Set up | a spare PC, or a VM (81/141) with a blank disk; backups reachable; nothing else | 15 min |
| 1. Base OS | Ubuntu install, user, SSH key, network (1–9) or the Ansible playbook (140) | 20–30 min |
| 2. Secrets | restic/rclone passwords, .env files, API tokens — from the off-server copy | 10 min |
| 3. Data | restore /etc bits, ~/docker, data folders, database dumps | 1–3 h |
| 4. Services | compose up, each stack; proxy; certificates | 30 min |
| 5. Test | a checklist per service, from a client device | 30 min |
| 6. Scorecard | what failed, what was missing, how long; fix and re-note | 20 min |
Rules of the drill
- Only the backups. No copying from the live server. If it isn't in a backup, it's lost — that's the point.
- Secrets must come from somewhere other than the server: the password manager (43), the printed sheet, the USB in a drawer. If the restic password only lives on the server, you've already found the first gap.
- Don't touch the live server's IP or DNS. The drill box gets a different IP; test by pointing a laptop's hosts file at it.
- Time every phase. The number is the answer to "how long would we be down?"
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:
- restic repository password(s) (55, 116) and rclone crypt passwords (54)
- the .env files for each stack (59) — or the Ansible vault password
- Cloudflare/DuckDNS tokens (48, 147), ntfy/Telegram tokens (93, 118)
- Vaultwarden's own backup: you can't look up the restic password in Vaultwarden if Vaultwarden is in the backup. Keep that one on paper.
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:
- /etc — only the files you edited (nginx sites, samba, fail2ban, ssh, msmtp, fstab lines for data drives, netplan). Not the whole tree.
- ~/docker into place: compose files, .env (or from secrets), each stack's ./data.
- Big data (media, photos, Nextcloud files): to whatever drive the drill box has, or a subset — for the drill, a few GB proves the path works; note the full size and the restore speed to extrapolate.
- Database dumps: mariadb < dump.sql into fresh containers (65), not copied files.
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:
- Log in to Nextcloud; open a file; upload one.
- Play something in Jellyfin; a photo in Immich.
- Vaultwarden: unlock the vault with the app pointed at the drill box.
- Home Assistant loads; a Zigbee device would need the stick moved — note it.
- Pi-hole answers DNS; Samba share opens; Paperless shows documents.
- The backup script itself runs on the drill box and writes somewhere.
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:
| Item | Result | Fix |
|---|---|---|
| Total time to "all services up" | 4 h 10 min | — |
| restic password available off-server | no — on server only | printed, in the safe |
| Nextcloud .env | in vault ✓ | — |
| Frigate recordings | not backed up (by choice) ✓ | — |
| Vaultwarden restored and unlocked | ✓ | — |
| Immich: 400 GB restore from B2 would take 30 h | partial restore only | add local restic repo |
| Nginx sites for office.example.com | missing — 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.
