Files
homelab/README.md
T
erikandClaude Opus 4.8 f9358375b5 feat: sops decrypts via pre-generated SSH host key (works on boot #1)
- pre-generate jupiter SSH host key off-repo (~/.config/homelab/jupiter)
- derive age recipient via ssh-to-age, add to .sops.yaml, re-encrypt secret
- secrets.nix: sops.age.sshKeyPaths = host key (drop separate keyFile)
- gitignore private-key patterns
- README: ship host key via nixos-anywhere --extra-files; secret decrypts boot #1

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:48:33 +02:00

104 lines
4.6 KiB
Markdown

# homelab
Flake-based NixOS config. Host: `jupiter` (ZimaBlade, NAS + services).
## Structure
```
flake.nix # inputs (nixpkgs, disko) + nixosConfigurations
jupiter/configuration.nix # real host: imports + bootloader + data mount
jupiter/disk-config.nix # disko: OS-disk partitions + filesystems
jupiter/hardware-configuration.nix # PLACEHOLDER — kernel modules, regenerate on target
jupiter/services.nix # shared: users, ssh, samba, containers, caddy
jupiter/vm.nix # VirtualBox test image (jupiter-vbox)
```
Two configs from one service definition: `jupiter` (real host, disko-partitioned)
and `jupiter-vbox` (test OVA). Both import `services.nix`.
## Test in VirtualBox (no hardware needed)
```
nix build .#nixosConfigurations.jupiter-vbox.config.system.build.virtualBoxOVA
VBoxManage import result/*.ova --vsys 0 --vmname jupiter-vbox
VBoxManage startvm jupiter-vbox --type headless
```
Login `darman` / `test`. Forward ports with `VBoxManage modifyvm ... --natpf1`.
## First install on the ZimaBlade — nixos-anywhere + disko
Wipes the OS disk and installs the flake over SSH. No USB needed if the box
already runs Linux (ZimaOS) reachable by root SSH — nixos-anywhere kexecs into
an installer, partitions via disko, installs.
> ⚠️ The OS disk in `disk-config.nix` is WIPED. Set `device` to the OS disk
> ONLY (by-id). Back up / physically identify the NAS data disk first — it must
> NOT appear in disko. `lsblk -o NAME,SERIAL,SIZE,MODEL` to identify.
1. Set the real OS disk id in `jupiter/disk-config.nix`
(`ls -l /dev/disk/by-id`), and the data-disk mount in `configuration.nix`.
2. Add your login SSH pubkey to `users.users.darman.openssh.authorizedKeys.keys`.
3. Set the real samba password:
```
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
nix shell nixpkgs#sops -c sops secrets/jupiter.yaml # edit, commit
```
4. Stage the pre-generated host key so sops can decrypt on boot #1
(private key lives off-repo in `~/.config/homelab/jupiter/`):
```
install -Dm600 ~/.config/homelab/jupiter/ssh_host_ed25519_key \
/tmp/extra/etc/ssh/ssh_host_ed25519_key
install -Dm644 ~/.config/homelab/jupiter/ssh_host_ed25519_key.pub \
/tmp/extra/etc/ssh/ssh_host_ed25519_key.pub
```
5. Run from your laptop:
```
nix run github:nix-community/nixos-anywhere -- \
--flake .#jupiter \
--extra-files /tmp/extra \
--generate-hardware-config nixos-generate-config ./jupiter/hardware-configuration.nix \
--target-host root@<zimablade-ip>
```
`--extra-files` plants the host key before first boot (its age identity is
already a recipient in `.sops.yaml`, so `/run/secrets/samba_password`
decrypts on boot #1). `--generate-hardware-config` pulls the target's real
kernel modules into the placeholder. Commit the result. Reboot into NixOS.
Manual alternative (USB ISO): boot installer, `disko` the disk, then
`nixos-install --flake .#jupiter`.
## Rebuild after changes (the daily loop)
```
# from laptop, build + activate on jupiter over SSH:
nixos-rebuild switch --flake .#jupiter \
--target-host darman@jupiter --use-remote-sudo
```
Rollback: `nixos-rebuild switch --rollback`, or pick a prior generation at boot.
## Adding a service
Copy the `whoami` block in `oci-containers.containers`, swap image/ports/volumes.
Native NixOS module exists for many apps (Nextcloud, Jellyfin, Grafana...) —
prefer `services.<app>` over a container when available. Add a `caddy`
`virtualHosts` block to expose it.
## Notes
- Backend is Podman with `dockerCompat` — `docker` CLI works, no daemon.
- Samba keeps its own password DB. `services.samba` never sets it; a systemd
oneshot (`samba-smbpasswd`) provisions it. Host reads the password from
`/run/secrets/samba_password` (**sops-nix**); the VM falls back to plaintext
`/etc/samba/smb-password`.
- Secrets: `secrets/jupiter.yaml` is age-encrypted (safe to commit) to two
recipients in `.sops.yaml` — the **admin** key (edit on laptop,
`~/.config/sops/age/keys.txt`) and the **jupiter host** key (derived from its
SSH host key via `ssh-to-age`, decrypts at runtime). Private keys live
off-repo and are gitignored. Rotate/add recipients with `sops updatekeys`.
- Data disk: plain `fileSystems."/mnt/data"` in configuration.nix — kept out of
disko so it is never formatted. Reference by `by-id` / `by-uuid`.
- `system.stateVersion` = `26.05`, install-time schema. Do NOT bump on upgrades.
- Terraform is not used: a single bare-metal box has no provider API. disko +
nixos-anywhere cover provisioning natively.
```