- 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>
104 lines
4.6 KiB
Markdown
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.
|
|
```
|