nix files for:
- my mac
- remove exe.dev machine
One-time steps that place secrets; backups persist them across recreations
(confirm a snapshot ran: cat /var/log/backup-cron/current). nikita's ssh key
(~/.ssh) and the tailscale authkey are both backed up, so neither is
re-placed on recreation. Each tailscale node's state lives in its own statedir
on the persistent disk but isn't backed up, so a fresh machine registers new
nodes; use an ephemeral key so retired ones auto-clean (see step 3).
-
Create the VM with the backup env vars below attached.
-
Generate nikita's per-machine ssh key and register it with GitHub as both auth and signing key:
ssh-keygen -t ed25519 gh ssh-key add ~/.ssh/id_ed25519.pub --title exedev --type authentication gh ssh-key add ~/.ssh/id_ed25519.pub --title exedev --type signing -
Create the tailscale secret: an OAuth client with the Keys → Auth Keys: write scope, tagged
tag:computer. The tailnet policy must define the tag and allow ssh into it:Tailscale Services are configured in the policy, not on the box. If your tailnet requires the Service object to exist before it can be hosted, add each once under Services → Add a service (
svc:cptr,svc:open-webui) — a one-time tailnet step, not per-machine.Place the secret on the machine (ephemeral, so retired VMs' nodes auto-clean):
sudo mkdir -p /var/lib/tailscale sudo sh -c 'umask 077; printf %s "tskey-client-…?preauthorized=true&ephemeral=true" > /var/lib/tailscale/authkey'Reboot (or run the per-node
tailscale upfrommodules/exedev/services/tailscale.nixby hand), thentailscale ssh nikita@computerover the tailnet. The authkey is backed up so you don't re-place it, and it registers every node. Each tailscaled keeps its node key (and, for the ssh node, its SSH host keys) in its own persistent statedir (not backed up), so a fresh machine registers new nodes; the ephemeral key lets retired ones auto-remove once offline — no manual cleanup. -
Enable HTTPS Certificates (admin console → DNS → Enable HTTPS, needs MagicDNS on). Required to provision the
*.ts.netcerts. Thecomputernode'stailscale-serveservice re-asserts both on every boot:https://cptr.<tailnet>.ts.net/→ cptr dashboard, tailnet-private, a Tailscale Service (svc:cptr). Because the Service is defined in the tailnet, this URL is stable across recreations — a fresh machine just re-hosts it (auto-approved) even though it registers as a new node.https://open-webui.<tailnet>.ts.net/→ Open WebUI, tailnet-private, a Tailscale Service (svc:open-webui) with the same stable-across-recreations property. The app itself binds loopback only; this Service is its sole ingress.https://computer.<tailnet>.ts.net/<tenant>/→ ingress, public via Funnel (needs thenodeAttrsabove). Unauthenticated — see the note inhosts/exedev/default.nix. This one is named after the node, so on a recreation where the retired ephemeral node hasn't dropped yet Tailscale may suffix it (computer-1) until the stale one is culled; exe.dev's public share is the stable public path if that matters.
We have to store it outside of the machine to be able to restore everything else on startup.
| Variable | Description |
|---|---|
RESTIC_REPOSITORY |
B2 restic repo, e.g. b2:backups:exedev |
RESTIC_PASSWORD |
restic repo encryption password |
B2_ACCOUNT_ID |
B2 key id |
B2_ACCOUNT_KEY |
B2 application key (scope to the bucket) |