The rack in the drawing on the front page is real. This is what is in it, how it is set up, and what runs on it. Updated .
The gear
| Part | What it is | Job |
|---|---|---|
| Compute | Five ASUS PN52 mini PCs. Each has a Ryzen 9 5900HX (8 cores, 16 threads), 64 GB G.Skill Ripjaws DDR4-3200 and two 1 TB Samsung 980 NVMe drives. Together: 40 cores, 320 GB RAM, 10 TB raw NVMe. | Proxmox VE cluster. Every service runs as a VM here. |
| Storage | Ubiquiti UNAS | Bulk storage. Serves the media libraries to VMs over NFS. |
| GPU box | An older machine with a GPU | Transcription and compute jobs. |
| Network | Gateway and switch | Everything plugs into these. |
How it is set up
Git is the source of truth. Every host is described in an Ansible repo. Playbooks get reviewed, then Semaphore UI runs them. When a host drifts, I rebuild it instead of fixing it by hand. Secrets live in the same repo, encrypted with SOPS and age.
VMs, not containers. Each service gets its own VM, cloned from an Ubuntu 24.04 cloud-init template. The baseline playbook hardens every VM the same way. I tried LXC for Syncthing, but an unprivileged container can’t set the clock or the kernel settings the baseline expects. A VM passed on the first run.
The rule says no container runtime on managed hosts. Some software only ships as a Docker image, so a few hosts are approved exceptions: Harbor, Zitadel and Fizzy. The rule is stricter on paper than in practice, and the docs say so.
Private by default. Every VM joins my Tailscale tailnet. Internal services get HTTPS through Caddy with Tailscale certificates, bound to the tailnet only. The few things that are meant to be public go out through a Cloudflare tunnel. No ports are open at home for them.
Watched. Each host runs node_exporter and vmagent. Metrics go to VictoriaMetrics, dashboards and alerts live in Grafana, and critical alerts go to Telegram. The Proxmox nodes also report CPU and NVMe temperatures.
One login. Zitadel is the identity provider. Grafana and Semaphore sign in through it with OIDC.
What runs on it
| Service | What for |
|---|---|
| Proxmox VE | The hypervisor cluster everything else runs on |
| Semaphore UI | Runs the Ansible playbooks |
| PostgreSQL 16 | Database for the lab services |
| Zitadel | Single sign-on |
| Harbor | Private container registry |
| VictoriaMetrics + Grafana | Metrics, dashboards, alerts |
| GitHub Actions runners | Self-hosted CI for my repos |
| Fizzy | Kanban boards, from Basecamp |
| T3 Code | An always-on remote dev box for coding agents |
| CLIProxyAPI | One hub that holds the Claude and Codex logins for every machine |
| Audiobookshelf | My audiobook library, served from the UNAS |
| Syncthing | Keeps my org-mode notes in sync across devices |
| Chess | A chess app, deployed with Kamal |