Side reading
A restore drill
Repeat the exact file and database recovery checks twice a year, without touching the live copy.
Run this on a normal day, twice a year and after adding a stateful service. Note the start time. The result is a measured recovery time, not an assumption.
This annex uses chapter 11's root wrapper, exact Compose path and repository credentials. Do not substitute bare restic unless you have deliberately configured an equivalent recovery client.
1. Reach and check the repository #
$ sudo /usr/local/sbin/homeserver-restic snapshots
$ sudo /usr/local/sbin/homeserver-restic check
$ sudo /usr/local/sbin/homeserver-restic check --read-data-subset=1/12
Rotate the deterministic fraction from 1/12 through 12/12 across checks, or run a complete annual --read-data check. Stop if the repository or structural check fails.
2. Restore into a unique directory #
$ drill=$(mktemp -d /var/tmp/homeserver-drill.XXXXXX)
$ sudo /usr/local/sbin/homeserver-restic restore latest --target "$drill"
$ sudo find "$drill/srv/homeserver/data/photos" -type f | sort | head
$ sudo du -sh "$drill/srv/homeserver/data" "$drill/srv/homeserver/appdata/dumps"
Open several restored photographs and a video. Check every irreplaceable directory that your installation uses: upload/, library/ when Storage Template is enabled, and profile/. Confirm that deliberately excluded generated directories such as thumbs/ and encoded-video/ are absent.
3. Restore the Immich database through stdin #
The dump exists on the host, not in the database container. Feed it through stdin and use a scratch database:
$ sudo docker compose -f /home/admin/homeserver/immich/compose.yaml \
exec -T database createdb -U postgres restore_test
$ sudo bash -c 'docker compose -f /home/admin/homeserver/immich/compose.yaml \
exec -T database pg_restore -U postgres -d restore_test < "$1"' _ \
"$drill/srv/homeserver/appdata/dumps/immich.dump"
$ sudo docker compose -f /home/admin/homeserver/immich/compose.yaml \
exec -T database psql -U postgres -d restore_test -c '\dt'
Choose one meaningful table that exists in your installed Immich release and compare its row count with the live database. A table list alone proves only that some schema restored.
$ sudo docker compose -f /home/admin/homeserver/immich/compose.yaml \
exec -T database dropdb -U postgres restore_test
4. Restore every later database #
After chapter 13, repeat the same process for authentik.dump in the authentik-db container. Create authentik_restore_test, restore through stdin as user authentik, inspect a meaningful row count, then drop the scratch database. If you add another stateful application, add its tested dump here at the same time.
5. Locate both recovery secrets #
Find the off-server copy of the age private key and the separate restic repository password. The first decrypts committed configuration; the second decrypts backup data. Check that each copy is readable without depending on the home server. Do not print either secret during the drill.
6. Clean up only the directory you created #
$ case "$drill" in /var/tmp/homeserver-drill.*) sudo rm -rf -- "$drill";; *) false;; esac
The guard refuses an empty, broad or unexpected target. Record:
- the date and total time;
- the snapshot restored and data fraction checked;
- each file category and database verified;
- anything missing, surprising or looked up;
- the corrective change and the successful repeat run.
The drill fails if the repository is unreachable, expected content is absent, a database does not restore, a recovery secret cannot be found, or the procedure depends on undocumented knowledge. Fix the cause and repeat from step 1; a run repaired halfway through is useful diagnosis, not a passing drill.
My restore drill failed
Identify the failed recovery layer without risking live data
Paste this into a new agent session. It carries everything the agent needs to know about where you are, and asks it to walk you through the problem rather than fix it for you.
/var/tmp/homeserver-drill.* directory and one check failed. I have not changed live data. Help me distinguish repository access, snapshot selection, exclusions, dump creation and database restore before I clean up.