darmanandClaude Sonnet 5 5507dfac0a neptun: add Headscale + Headplane
Headscale is the tailnet control server every host's services/tailscale.nix
already points at (--login-server=https://vpn.mgaction.town). MagicDNS
base_domain "hosts.mgaction.town" matches the "jupiter.hosts.mgaction.town"
names already used in this repo's Caddy vhosts.

Headplane is its web UI, running as headscale's own user (native process
integration, no container). No OIDC wired up - log in with a headscale API
key generated on the box. Both proxied through Caddy; headscale's vhost
needs flush_interval -1 since its node-update endpoint is a long-poll.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 22:36:40 +02:00
2026-07-19 22:36:40 +02:00
2026-07-19 22:36:40 +02:00

homelab

Flake-based NixOS config. Host: jupiter (ZimaBlade, NAS + services).

Structure

flake.nix                  # inputs + nixosConfigurations (jupiter, neptun, kexec, ...)
common.nix                 # shared base: user, ssh, nix, firewall, timezone
services/                  # one reusable module per service
  samba.nix  avahi.nix  audiobookshelf.nix  containers.nix  caddy.nix  tailscale.nix
hosts/
  jupiter/                 # ZimaBlade NAS
    configuration.nix      #   host bits + imports common + the services it runs
    disk-config.nix        #   disko: eMMC partitions
    hardware-configuration.nix
    secrets.nix            #   sops-nix wiring
    vm.nix                 #   VirtualBox test image (jupiter-vbox)
  neptun/                     # netcup public reverse proxy + tailnet node
    configuration.nix  disk-config.nix  hardware-configuration.nix  secrets.nix
secrets/                   # age-encrypted sops files (jupiter.yaml, neptun.yaml)
scripts/                   # deploy, edit_secrets

Hosts compose by importing common.nix + whichever services/* modules they run. Each service module opens its own firewall ports.

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 hosts/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 ./hosts/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.

Deploy (the ./deploy wrapper)

All arguments mandatory — no default host, no default config.

./deploy kexec   <host>            # headless kexec into a RAM installer (RO-root box)
./deploy install <config> <host>   # first install; wipes OS disk, ships host key
./deploy switch  <config> <host>   # rebuild + activate on a running host
./deploy boot|test <config> <host> # stage for next boot / activate without boot entry

<config> is a nixosConfigurations name (jupiter, neptun). Its pre-generated SSH host key lives at ~/.config/homelab/<config>/ssh_host_ed25519_key.

Examples:

./deploy switch jupiter jupiter.sol
./deploy install neptun 159.195.64.117

Rollback: nixos-rebuild switch --rollback on the host, 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 dockerCompatdocker 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.
S
Description
No description provided
Readme
32 MiB
Languages
Nix 40.1%
QML 30.5%
Shell 25.9%
Python 3.5%