Design principles¶
The rules this homelab is run by. When a new decision comes up, check it against this list first.
The repo is the server¶
The Git repository is the single source of truth. Machines are never fixed by editing config on a host. Changes are made in the repo, validated, deployed, and committed. If a host and the repo disagree, the repo wins; the host gets rebuilt to match.
Zero open ports¶
No application port is ever exposed to the public Internet. External traffic enters only through Cloudflare Tunnel → Caddy, which means the firewall stays closed and the attack surface is the tunnel, not the hosts. See the security model for the full picture.
The tailnet is the internal boundary¶
Hosts talk to each other over Tailscale, and tailnet membership is what makes a machine "inside". Operator-only surfaces such as Beszel are gated by tailnet address ranges, not passwords on public endpoints.
Secrets never touch the store¶
Plaintext secrets never go into .nix files or the Nix store. Everything
sensitive lives in sops-encrypted YAML, decrypted at activation time by each
host's own SSH key. A leaked repo leaks nothing.
Pin everything¶
- Container image tags are pinned, never
latest. flake.lockpins the entire system; it is updated deliberately withnix flake update, never edited by hand.system.stateVersionrecords install-time defaults and is not bumped.
Keep yggdrasil light¶
The edge node has 4 GB of RAM and one job: routing and observing. Applications
belong on application hosts (midgard or alfheim), not on yggdrasil. If a
new service is not ingress or monitoring, it does not belong on yggdrasil.
NixOS modules first¶
Prefer NixOS modules for infrastructure and apps that package cleanly that way. Reach for OCI containers only when upstream genuinely packages better as a container and the target host deliberately has Podman enabled.
One change at a time¶
Small, focused changes: edit the repo, open a PR, let CI build every host,
merge once green, then let CD deploy. Local just test / just switch is only
for explicit break-glass or bootstrap requests. Two unrelated changes never
ride in the same deploy.