Restoring a Host from Backup#
Quick Reference#
| NixOS | Darwin | |
|---|---|---|
| Backup tool | borgmatic (service) |
raw borg (launchd script) |
| Config/credentials | /etc/borgmatic.d/voile.yaml, /etc/borgmatic/key, /etc/borgmatic/pass |
/etc/borgmatic/run_backup.sh, /etc/borgmatic/key, /etc/borgmatic/pass |
| List archives | sudo borgmatic list |
borg list |
| Extract files | sudo borgmatic extract --archive <name> --path <rel-path> |
borg extract ::<name> <rel-path> |
| Archive naming | <hostname>-<timestamp> (borgmatic-generated) |
<hostname>-<ISO8601> |
| Switch command | nh os switch .# -H <hostname> |
nh darwin switch .# -H <hostname> |
Restoring Files — NixOS (borgmatic)#
After a full wipe and rebuild (see sops bootstrap below),
borgmatic is fully configured by activation. The borgmatic config is at
/etc/borgmatic.d/voile.yaml with the repo URL, SSH key, and passphrase already wired in.
List borgmatic archives#
sudo borgmatic list
Output looks like:
voile: Listing archives
mokou-2025-01-15T03:00:01.123456 Wed, 2025-01-15 03:00:01 [...]
mokou-2025-01-14T03:00:01.654321 Tue, 2025-01-14 03:00:01 [...]
Extract specific files (borgmatic)#
borgmatic extract always runs in the current working directory and reconstructs
the path hierarchy from root. Run from /tmp to avoid collisions:
cd /tmp
sudo borgmatic extract --archive mokou-2025-01-15T03:00:01.123456 \
--path home/tsunami/.config/sops/age/keys.txt
The file lands at /tmp/home/tsunami/.config/sops/age/keys.txt.
Extract the user sops age key — NixOS#
cd /tmp
sudo borgmatic extract --archive <latest-archive> \
--path home/tsunami/.config/sops/age/keys.txt
mkdir -p ~/.config/sops/age
sudo mv /tmp/home/tsunami/.config/sops/age/keys.txt ~/.config/sops/age/keys.txt
chmod 600 ~/.config/sops/age/keys.txt
Then re-run activation to decrypt user-level secrets:
nh os switch .# -H <hostname>
Full home restore#
To restore all of /home:
cd /
sudo borgmatic extract --archive <archive-name> --path home
This overwrites in place. Use --dry-run first to preview.
Borgmatic sources#
The NixOS borgmatic config backs up:
/etc/home/var/log/var/backup
Restoring Files — Darwin (borg direct)#
Darwin uses raw borg via a launchd-managed shell script. There is no borgmatic.
After rebuild and nh darwin switch, credentials are at the same paths as NixOS.
Set up environment#
The repo URL is in the backup script. Pull it from there:
grep 'BORG_REPO=' /etc/borgmatic/run_backup.sh
# → export BORG_REPO="ssh://borgwarehouse@voile.armadillo-banfish.ts.net:22222/./<repo-id>"
export BORG_REPO="ssh://borgwarehouse@voile.armadillo-banfish.ts.net:22222/./<repo-id>"
export BORG_RSH="ssh -i /etc/borgmatic/key"
export BORG_PASSPHRASE="$(cat /etc/borgmatic/pass)"
List borg archives#
borg list
Archive names look like: work-laptop-2025-01-15T03:00:00Z
Extract specific files (borg)#
borg extract also runs relative to the current directory. From /tmp:
cd /tmp
borg extract ::work-laptop-2025-01-15T03:00:00Z \
Users/bcraton/.config/sops/age/keys.txt
File lands at /tmp/Users/bcraton/.config/sops/age/keys.txt.
Extract the user sops age key — Darwin#
cd /tmp
borg extract ::<hostname>-<timestamp> Users/<user>/.config/sops/age/keys.txt
mkdir -p /Users/<user>/.config/sops/age
mv /tmp/Users/<user>/.config/sops/age/keys.txt /Users/<user>/.config/sops/age/keys.txt
chmod 600 /Users/<user>/.config/sops/age/keys.txt
nh darwin switch .# -H <hostname>
Darwin backup sources#
The Darwin backup script backs up / with extensive excludes for Nix store,
caches, Library folders, derived data, etc. See modules/darwin/borg/default.nix
for the full exclude list.
Sops Bootstrap After a Wipe#
Before borg is accessible, the system must activate successfully — which requires
secrets.yaml to be encrypted for the new SSH host key. This is the bootstrap
procedure that unblocks everything above.
Key architecture#
| Layer | Key source | Protects |
|---|---|---|
| System | /etc/ssh/ssh_host_ed25519_key |
Borg SSH key, borg passphrase, nix cache key, builder key |
| User (home-manager) | ~/.config/sops/age/keys.txt |
GitHub SSH key, taskchampion, Obsidian API key, etc. |
After a wipe, the SSH host key changes, breaking system-level decryption. You must
re-encrypt secrets.yaml for the new key before switching.
Phase 1 — From an authorized host (e.g. mokou)#
1. Get the new host age key#
On the wiped machine, once nix is available:
nix shell nixpkgs#ssh-to-age --command \
sh -c 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'
2. Update .sops.yaml#
keys:
- &hosts:
# ...
- &myhostname age1<new-key-from-step-1>
3. Re-encrypt secrets.yaml#
sops updatekeys secrets.yaml # review the key diff, confirm with y
git add .sops.yaml secrets.yaml
git commit -m "Update <hostname> sops key after machine wipe"
git push
Phase 2 — On the wiped machine#
git pull
nh os switch .# -H <hostname> # NixOS
# nh darwin switch .# -H <hostname> # Darwin
sops-nix activates using the SSH host key and places all system secrets, including
the borg credentials at /etc/borgmatic/key and /etc/borgmatic/pass.
Borg is now accessible — proceed with file extraction above.
Generating a new user sops key (if old key unrecoverable)#
On the wiped machine:
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
# Note the public key: age1...
On an authorized host, update .sops.yaml:
keys:
- &users:
- &username-hostname age1<new-public-key>
sops updatekeys secrets.yaml
git add .sops.yaml secrets.yaml
git commit -m "Rotate <user>-<hostname> user sops key"
git push
Back on the wiped machine:
git pull
nh os switch .# -H <hostname>
Troubleshooting#
sops activation fails after switch#
git log --oneline -3 # confirm updatekeys commit is present
sops --decrypt secrets.yaml | head -5 # test decryption directly
Borg credentials missing after activation#
ls -la /etc/borgmatic/key /etc/borgmatic/pass
If missing, sops activation silently failed. Check that tsunaminoai.borg.enable
is set and that borgmatic/<hostname>/key and borgmatic/<hostname>/passphrase
exist in secrets.yaml.
borgmatic: no archives found#
The host may not have backed up yet (new repo) or the repo wasn’t initialized.
Check /var/log/borgmatic.log and look for the init-borg-repo systemd unit:
systemctl status init-borg-repo
journalctl -u borgmatic
borg: Permission denied on key#
The SSH key at /etc/borgmatic/key is 0400 root:root. Run borg as root, or copy
the key to a temp path with user-readable permissions for one-off access:
sudo cp /etc/borgmatic/key /tmp/borg-key && chmod 400 /tmp/borg-key
export BORG_RSH="ssh -i /tmp/borg-key"