Projects / ongoing

The Homelab

A UniFi network running a NixOS fleet from one flake: workstation, services host, media, a libvirt security lab, a staging/CI box that moonlights as a Minecraft server, and an off-site Linode node fronting it, all deployed with a single colmena apply. One Ubuntu NAS stays off NixOS on purpose.

Active
  • NixOS
  • Colmena
  • sops-nix
  • Terraform
  • libvirt
  • UniFi

UniFi runs the network; NixOS runs everything on it. The Proxmox migration is finished. Every server was wiped to NixOS and now runs on bare metal, with no hypervisor underneath. What used to be a pile of standalone machines is now one flake, one colmena apply: the whole estate described in a single repo, deployed from one workstation, with secrets in sops-nix and fresh installs handled by disko + nixos-anywhere. There is no wiki for the lab. This page is the closest thing it has to current documentation; for the declarative hosts, the config is the documentation.

Base topology

internet unifi gateway unifi switching trusted lan iot guest desktop mgmt media hacktop nas · ubuntu tv tv homepod guest devices cloud1 · linode
Tap or hover a node to inspect it; select playground to expand its security-lab VMs. Green ticks are online status.

The trusted network is where the fleet lives:

HostRole
desktopNixOS workstation and gaming rig, also the Colmena deploy controller
mgmtInternal DNS, a private step-ca CA, the homelab SIEM, and Forgejo
mediaNixOS media host: the media stack run declaratively; storage over NFS
playgroundNixOS + libvirt security lab: Kali, Parrot, and a malware-analysis bench
hacktopStaging, the self-hosted CI runner, and the fleet’s Minecraft server
nasUbuntu 24.04 LTS: bulk storage over NFS, kept off NixOS on purpose

Click playground in the map to expand its security-lab VMs. Segmentation is UniFi policy at the gateway: the IoT network holds the smart TVs and a HomePod that can reach the internet but not the lan, and visitors land on a guest network with internet access and nothing else.

A handful of shared NixOS modules do the load-bearing work: a common baseline (key-only SSH, nftables, a hardened deploy user, sops wiring, and a journal shipper), a module that trusts an internal step-ca certificate authority and points security.acme at its private ACME endpoint, and a SIEM-agent module that enrolls any host by being switched on. Wiring NixOS to a private CA is uncommon; the payoff is that every internal service gets a real, auto-renewing certificate without a public name ever leaving the house.

Secrets are sops-nix, and each host decrypts its own with no key distribution at all: its age identity is derived from its existing SSH host key. Colmena pushes closures through a dedicated deploy user, a Nix trusted-user with scoped NOPASSWD sudo for only the activation commands, so a stolen deploy key can re-activate a build but can’t open a root shell.

Off-site, same flake

The estate doesn’t stop at the house. cloud1 is a Linode node, Terraform-provisioned and installed greenfield with nixos-anywhere, then folded into the same flake and Colmena hive as the LAN boxes — one repo managing the house and the cloud. It has already lived one full lifecycle: stood up, torn down when it had no job, and rebuilt from the same code the day it got one. That round trip is the part worth noticing — when the infrastructure is code, decommissioning costs nothing and coming back is a terraform apply and a colmena apply. The SIEM went declarative too: the fleet moved off Wazuh onto a leaner Loki/Alloy stack (siem-lite) — Alloy ships every host’s journal to Loki on mgmt, and Alertmanager pushes to a self-hosted ntfy topic. That cutover was gated, because mgmt carries the LAN’s DNS and PKI and a bad deploy there is felt everywhere.

A public game, no open home ports

The job that brought cloud1 back: play.briggsbastian.com, an All the Mons modpack server (Minecraft 1.21.1, NeoForge, 373 mods) I run for my friends. It lives on hacktop — the box with the RAM headroom — as a fully declarative nix-minecraft service, the modpack’s server files pinned into the Nix store like any other dependency. Publishing it never touches the home firewall: hacktop dials out to cloud1 over WireGuard and holds the tunnel open with keepalives, and nftables on the Linode DNATs public :25565 traffic down the tunnel. No inbound rule at home, the home IP never published, and every drive-by scan on the internet lands on the cloud box instead of the house — all for ~10–25 ms of added latency. Why I like this shape, and where it stops scaling, is a garden note.

The security lab

playground is a libvirt host carrying the offensive and reverse-engineering VMs, reachable through a clientless, in-browser Guacamole session: Kali and ParrotOS are live for attacking the lab, with REMnux and a Windows FLARE bench (TPM 2.0 emulated via swtpm under OVMF) next in for pulling samples apart. It’s the range for purple-team work: attack the fleet from the Kali box, confirm the SIEM caught it, and where it didn’t, write the rule that does. On NixOS the cleanup is one command: redeploy the compromised host to a known-good state. The loop is attack, detect, rebuild.

What it proves

  • Declarative where it counts. Every NixOS host is rebuildable from its configuration rather than from memory, and rolled back just as cheaply, since generations are free. The one undocumented machine in the lab is the one that isn’t NixOS yet.
  • A real deploy story. One repo, shared modules written with the module system (mkOption/mkIf/mkEnableOption rather than copy-paste), sops-managed secrets, and a staging host (hacktop) that builds and verifies a config, and now runs the CI that gates it, before anything is promoted to production. That staging-then-promote loop is the change-management process.
  • A mixed estate on purpose. The NAS stays Ubuntu, the one box I keep off Nix on purpose, because “I ported service X from Ansible to a NixOS module and here’s what each got right” is a better story than “I use Nix for everything.”

The whole estate is where I practice the SecDevOps habits I’m building in the open. Running notes are in the thought garden, and the evidence is on the ops page.