TL;DR
- A single Lenovo M710q Tiny runs Debian 13 and handles NAT, Tailscale subnet routing, a five-camera Frigate NVR, HomeAssistant, Pinchflat, and a few Grafana dashboards.
- The hardware bill is about $585, not counting the bulk outdoor Cat6 I terminated myself and lost track of.
- Some workloads have to live outside the Kubernetes cluster: gateway duties, USB Coral TPU inference, stateful home automation, and the dashboards I check when the cluster is broken.
- The real lesson is that owning my family’s video data matters more than saving a few bucks. I’m not handing that to a cloud NVR and paying them for the privilege. Nah, I’m good partner.
What I built
I picked up a Lenovo M710q Tiny in December 2024 for $148: Core i5-6500T, 32 GB DDR4, 512 GB SSD. It runs Debian 13 with a LUKS-encrypted root and a handful of Docker Compose files. The box sits upstream of my Talos Kubernetes cluster on the isolated wired LAN. Its WiFi interface is the only internet uplink for that subnet, so it NATs traffic out with a simple iptables MASQUERADE rule and advertises the subnet into Tailscale. That is how I reach the cluster from anywhere on the tailnet.
It also runs the stuff I don’t want inside Kubernetes. Frigate handles five cameras through go2rtc and a Google Coral USB TPU. HomeAssistant manages the house. Pinchflat archives YouTube channels. A few Grafana dashboards and monitoring exporters live here too.
Hardware bill
| Item | Cost |
|---|---|
| Lenovo M710q Tiny (i5-6500T, 32 GB DDR4, 512 GB SSD) | $148 |
| 4 outdoor PoE Amcrest cameras at $58 each | $232 |
| Google Coral USB TPU | $140 |
| Netgear GS308EP 8-port PoE+ Gigabit switch | $65 |
| Total | ~$585 |
I left the outdoor Cat6 cable out of that number. I bought a several hundred feet in bulk and terminated the ends myself, so I saved money but I honestly lost track of how much of it went to this project, and how much has been used in other project, AND I lost track of how much the cable cost in bulk.
Three of the four cameras are deployed outside right now. The fourth is still sitting in a box while I decide where it goes.
What runs here, and why not in the cluster
NAT gateway and Tailscale subnet router. The isolated wired LAN has no direct internet egress. Something has to sit on the edge, NAT out through WiFi, and advertise the subnet into Tailscale. I tried running Tailscale as a Talos system extension for a while, but the ext-tailscale service stalled on every boot. After I removed it through a rolling Talos image upgrade and moved the job here, the boot reliability problem went away.
Frigate and the Coral USB TPU. Five cameras in Frigate: three outdoor PoE Amcrest cameras on the wired LAN, plus two indoor Reolink WiFi cameras that I bridge with Neolink. The Reolinks are on the house network and not reachable from the wired LAN, so Neolink talks to them over HTTP on port 9000 and exposes RTSP streams on localhost. Frigate reads those local RTSP streams. All detection goes through the Coral USB TPU, which dropped inference from about 36 ms CPU-only down to about 11 ms on the edgetpu. Talos could probably pass the USB device through to a node, but USB passthrough into an immutable Kubernetes node is brittle. On Debian it is just a Docker container with a device mapping and a udev rule.
HomeAssistant. It is stateful, updates frequently, and expects a mutable filesystem with add-ons. My use case maps poorly onto ephemeral pods.
Pinchflat. Long-lived container, local storage, simple updates. No need for orchestration.
Grafana dashboards. This one is partly historical and partly practical. Prometheus lives in the cluster, but the dashboards are here because this is the box I look at first when Kubernetes is misbehaving. If the cluster is down, I still want the graphs. The historical part is that this box used to be wired up to an big T.V. that sat on a shelf in my office. This T.V. didn’t have a place in my house, so one day I plopped it up there and had SwayWM autostart in kiosk mode with specific dashboards autotiled. So I could just glance up to see my grafana dashboards, a list of my tasks, etc.. This T.V. finally died, but the dashboards still live here.
Networking reality
The wired LAN is a flat 192.168.2.0/24 L2 network. The Debian gateway, the Talos nodes, the outdoor PoE cameras, and the Cilium L2 LoadBalancer pool all share the same broadcast domain. There are no VLANs because the Debian server has a single ethernet NIC into the Netgear GS308EP switch. Isolation between cluster workloads and cameras is IP-based. The network is RFC1918 with no inbound NAT from the WAN, the only external path in is Tailscale, gated by ACLs.
The cost of being upstream of the cluster
Because Debian sits upstream of the cluster, when it goes down the cluster loses internet egress, tailnet reachability, and the alerting path. The LUKS reboot postmortem documents such a time when this happened. The TLDR of that postmortem is that there was a power outage (before I installed an UPS) and a LUKS passphrase prompt sat on the screen until I woke up and physically entered the password the next day.
What I learned
Some services are just easier to run on mutable Linux. Talos eliminates a lot of maintenance, but a gateway box that needs tweaking, inspecting, and occasional fixes at odd hours benefits from being able to drop into a shell and change things.
The bigger lesson is about data ownership. I think video of my family coming and going from our own house should be data that my family controls. I want to own that, not hand it to a cloud provider and then pay them monthly for access to it. That is why I built the camera stack. Everything else is just what happened to land on the same box.