What runs, and why
A tour of the services in the apartment datacenter, sorted by what they are for rather than by vendor — with the reason each one is there and the things that were deliberately left out. This is what runs as of 2026-09-08, not a recommendation; every guest named here was seen running on the Proxmox API on that date, and every container comes from a compose file in git.
Two computers under a desk in Cologne run the things a household usually rents from Google, Apple or Amazon. Nothing here came as a bundle: every service on the list is here because something in the flat needed it, and everything that can be pinned to a version, rebuilt and rolled back is.
The shape of it
Two physical Proxmox nodes carry everything. A Synology NAS — the storage box above — sits beside them for shared storage, backups, the cluster’s tie-breaker vote and a handful of services of its own. Everything that runs on the nodes is a guest: one virtual machine or one container. Guests come in two kinds: a handful of Docker VMs for stacks that want to be composed together, and small LXC containers for single-purpose services that need nothing but an operating system and one daemon.
What runs where, by role — the guest list as of 2026-09-08 first, the NAS’s own services after it:
| Role | What runs |
|---|---|
| Edge | two OPNsense VMs, one per node — the firewall pair that owns the WAN legs |
| Smart home | one Docker VM (tychome) with Home Assistant and the voice stack |
| Cameras | a detection guest on the second node, beside the recorder on the NAS |
| Sites and ingress | one Docker VM (tycfront) for the Traefik front door and the static sites |
| Observability + CI | one Docker VM (tycdocker) carrying the Grafana stack, plus tycops, the VM the stack is moving onto |
| Secrets | an OpenBao LXC (tycbao) |
| Media | a Plex LXC |
| Network services | the UniFi controller LXC, an mDNS reflector LXC, a UPS-monitor LXC |
| Backup | a backup-server VM, and since September the nightly target for every guest but two |
| Development | one VM for development work |
| CI runners | two general-purpose runners on the Docker host, four runner containers on the nodes, plus one on the NAS as the recovery path |
| NAS services | the household photo library and file sync, the identity provider behind the Proxmox login, a network-boot container and the two backup applications — on the NAS itself, not as guests |
One guest still runs twice over: the host that carries both observability and ingress is up beside the two new guests taking over its halves. That is the migration, not an accident: each replacement runs beside its predecessor until a separate, reviewed retirement, and the architecture note explains why the cutover is staged that way.

The room all of this runs in — real light, real hardware, no render.

August 2026: the switches mounted vertically in the gap between the desk and the hi-fi tower; the final cabling is still to come.
Smart home
Home Assistant is the hub, and it runs as a plain container rather than Home Assistant OS. The reason is pinning: HAOS add-ons cannot be version-pinned, rebuilt and rolled back like any other service, and everything else here can. The price is that the “add-ons” have to be run as ordinary containers alongside it — which is exactly what the voice stack is.
- Mosquitto — the broker for MQTT, the small message protocol devices use to post their readings so that anything interested can subscribe to them. Only a handful of devices and bridges here need it; the Zigbee radio and the DIY sensors talk to the hub directly instead.
- Matter server — the Matter fabric for the devices that speak it.
- Zigbee and Thread — the Zigbee radio is driven by the hub itself, and Thread has no stack of its own: it rides the border router built into the same coordinator, and the deployment refuses to stand a second one up beside it. The four radios in the house note has the detail.
- ESPHome — the firmware builder for the DIY sensors; its dashboard is the git checkout, so device configs are versioned like everything else.
- tycwled — the household’s own firmware for the two dodecahedron lamps in the photo above. The vendor ships a closed fork of WLED whose effects come with no source, so they were read back out of the factory image and reimplemented on stock WLED, published under the MIT licence. Every change has to prove itself against a second lamp still running the vendor build.
- Whisper and Piper — speech-to-text and text-to-speech, locally. Nothing the local assistant hears leaves the machine. It is not what the household actually talks to: the Amazon Echo speakers still in the flat carry the everyday spoken commands, and they are cloud devices by construction.
- Music Assistant — the multi-room audio layer for the speakers.
- govee2mqtt — the bridge that keeps the Govee lights on the local broker instead of a cloud app.
What the house does with all of that hangs off one variable. A house state is derived from the front door, the phone and the presence sensors — never from a clock — and the welcome light, the lock, the air conditioning, the cameras, the vacuum and the bathroom fan all read it. That has its own note: the house decides, and no clock does.
Home Assistant is also the one service that deploys itself, and the one
place in the house where a change reaches the branch without a reviewer:
dashboards edited in the hub’s own interface are committed straight back to
main, because losing every interface edit on the next sync was judged
worse than the missing review. The fail-closed check the container runs
before it accepts a commit is what stands in for the reviewer. The whole
loop is in the note on
configuration that deploys itself.
Cameras and detection
The cameras record to the surveillance software on the NAS, as they always have. Beside it runs a second, local detection plane: an object detector in its own guest on the second node, using that machine’s integrated graphics for inference and keeping no recordings of its own. The split is deliberate. The detector detects, Home Assistant decides what a detection means, and the surveillance software keeps recording and raising alarms.
Every camera in the flat is enrolled now, the fisheye at the front door included, and the detector recognises faces on the same stream it detects on — so an alert raised while the flat is empty can say who it was rather than only that somebody was there. What the hub does with a detection, and why the recorder’s own Home Mode is deliberately left alone, is in the house decides.
Energy
The house total is read from the utility meter itself: a small radio reader sits on the meter’s optical port, runs firmware built in the flat, and publishes to the house broker with no vendor account anywhere in the path. The three current clamps that used to carry that role are now only a cross-check. The loads worth watching still report per socket or per circuit, all of it inside the hub. What keeps the picture honest is that no load is counted twice — the metering rules, and what is still owed, are in the four radios in the house note. The figures themselves stay off this page.
Media
Plex serves the household library from an LXC; the bulk media stays on the NAS and is mounted in. Synology Photos runs on the NAS itself and holds the household’s photo library — the pictures stay in the flat rather than on a photo service’s servers.
Sites and ingress
Traefik is the public web front door. It routes this site, a second Hugo site and a couple of pro-bono ones. Every one of them is a container image pinned by tag and digest — the second site moved off a copied folder onto the same image path, so they all ship the same way now. What is served changes only when the pin changes in git and the gated apply runs. Public ingress is IPv4 only, and that is now a decision rather than a stage: publishing these names over IPv6 as well was ruled out in September.
Homepage is the internal dashboard — the page that answers “where was that again” for the household.
The front door is in the middle of a move: tycfront is the new ingress VM
in the published zone. The sites are served from both during the coexistence
period, and the copies retire once the move is reviewed.
Identity and secrets
OpenBao holds every credential the fleet uses. No merge-request pipeline ever holds a long-lived token. Each job proves who it is with an OIDC identity — a signed statement from GitLab that this job is this job — against a per-repository, read-only role, and receives a short-lived credential scoped to exactly that job. Plan material and apply material are separate roles. The few long-lived tokens that remain — the ones that talk to the forge and the package hosts rather than to a machine — live in the vault and are handed to protected-branch jobs as masked variables, never typed by hand. The vault takes a nightly snapshot to the NAS, and its restore job has actually been run; only the smart-home hub can say the same, and most things on this page cannot.
Synology SSO Server on the NAS is the identity provider behind the Proxmox login: the cluster carries a realm that maps the NAS’s administrators group onto a scoped admin role, and that mapping is declared in the infrastructure code like the rest of the cluster. The application itself is set up by hand in the NAS’s own interface — the mapping goes through review, the thing that authenticates does not.
Observability
Grafana, Loki and Prometheus, with Alloy shipping logs and metrics from the hosts the configuration management reaches, plus the usual exporters (node, Proxmox). The firewalls, the switches and the modem have no agent to install and send their logs the old way instead. The Grafana alert rules are provisioned from git like everything else. Coverage is incomplete: fleet coverage is a tracked work package rather than a finished state.
Network services
Most of the network is not a service at all — it is the RouterOS fabric and the OPNsense pair, both driven from git and written up in the architecture note. The parts that run as services of their own:
- Kea DHCP and Unbound DNS on the edge pair — Kea hands out the addresses, Unbound answers the name lookups — one set per zone, with the host registry generated from the same network model that generates the switch configuration. The resolver also filters advertising and malware domains for the zones that should have them. Filtering only works if it cannot be walked around, so the firewall sends those clients back to the resolver, refuses the encrypted lookup protocols outright, and the resolver blocks the public services that answer them. A filter a device can step around is theatre. There is no Pi-hole; the resolver lives where the policy lives.
- A WireGuard tunnel — the household’s own remote access into the flat terminates on the edge pair.
- UniFi controller — one LXC for the one surviving UniFi device, the access point.
- mDNS reflector — so casting and discovery cross the zone boundary where it is explicitly allowed to.
- UPS monitor — a small web interface over NUT, the daemon that reads the uninterruptible power supply; the nodes read it too, so that they can shut down cleanly when the power goes.
- netboot.xyz — network boot: a machine with no installer to hand pulls one over the wire. It is a container on the NAS rather than a guest on the nodes, committed as a compose file and started by hand in the NAS’s container manager, the same shape as the runner kept there for recovery.
- Cloudflare is the DNS authority, managed as code; the registrars are only registrars.
CI
CI is the pipeline that checks a change and then carries it out; GitLab runners are the machines it runs on, and they sit where the work is. General-purpose ones take merge-request and build work. Dedicated ones run as their own containers on the nodes: one for the infrastructure applies, one for the smart-home repository, and one per edge node, because each firewall’s own identity — its interfaces, its addresses, its dial-up session — has to be written from the node it lives on. One more is a container on the NAS, kept for a single purpose: being the path that still works when both nodes are down. Apply jobs are manual, environment-scoped and serialised per OpenTofu state; merge-request pipelines are read-only.
Storage and backup
The NAS shares storage over the network two ways: NFS, which the guests mount for shared storage, and SMB, which the Home Assistant backups are written over. The nodes keep the repair-path guests on their own disks instead, on ZFS — a filesystem that checksums what it stores — so that the NAS is not on the path to recovering the NAS.
The household’s own files live on the NAS too. Synology Drive keeps them in step across the machines that use them. Active Backup for Business captures the household workstation, and Hyper Backup replicates from the NAS to a target in another building — the off-site copy. All three are set up by hand in the NAS’s own interface, not through the reviewed pipeline the rest of this page runs on. What those copies cover, and what is still owed, is the backups note’s question rather than this page’s: Backups: three copies, one missing proof.
What was deliberately left out
- Kubernetes. No workload here needs it; it comes back on the day one does.
- Home Assistant OS. See above — pinning beats convenience.
- A cloud backup target. Not yet, and not before a restore has been proven against the copies that already exist.
- Sponsorship. Nothing on this page is paid for or for sale.
Those four are decisions rather than gaps, and most of them name the condition that would reverse them. Nothing on this page runs because it was expected to be here: it runs because something in the flat needed it, and the day that stops being true is the day it comes off the list.
Guest inventory, storage and the nightly backup jobs read from the Proxmox API on 2026-09-08, and the household automations from the running hub the same day; the containers against the compose files in git, and the runner list against the declarations that register them. The identity provider’s realm on the cluster is declared in the infrastructure code and can be read there; the NAS’s other applications are as the operator stated them on 2026-09-09, and were read from neither the NAS nor the code. Planned items are marked as planned.