TL;DR
- Three refurbished Lenovo M700 1-liter PCs run a bare-metal Talos Linux Kubernetes cluster for about $330 total.
- The platform layer is Cilium (CNI, Gateway API, L2 LoadBalancer), Longhorn, cert-manager, ExternalDNS, and kube-prometheus-stack.
- The whole cluster lives on an isolated wired subnet with no direct internet egress; a Debian gateway handles NAT, Tailscale access, and the single point of failure that already taught me some hard lessons.
- I switched from k3s because Talos makes more sense to me and I was tired of managing the OS underneath.
What I built
The cluster is three Lenovo M700 mini-PCs. Each one has a quad-core Intel Core i5-6500T up to 3.1 GHz, 16 GB DDR4, and a 512 GB SSD. They’re roughly ten-year-old refurbished machines that cost $110 each, so the whole cluster came in at $330. They’re small, quiet, and have handled everything I’ve thrown at them so far.
Network-wise, the cluster sits on an isolated 192.168.2.0/24 wired LAN with no direct internet egress. The Debian gateway is the only path in or out; it NATs traffic with iptables MASQUERADE and advertises the subnet through Tailscale. Everything plugs into one Netgear GS308EP switch on a flat L2 broadcast domain. Cilium handles CNI, network policy, Gateway API, and L2 announcement for the LoadBalancer pool.
Why Talos
I ran k3s on this hardware first. I honestly can’t remember the real reason I moved, other than Talos just makes so much more sense and I was tired of managing the underlying operating system. Maybe I wasn’t tired of it, but tired of how it distracted from what I was trying to grasp more solidly which is Kubernetes itself. I still enjoy regular system admin tasks on my Debian server, but when I need to learn a new Kubernetes concept and play with an idea, I can’t have server maintenance distracting from that at the time. I cut over around the Talos v1.9.4 release, which puts it roughly at February 13, 2025. That’s the whole migration story.
What lives on the cluster
Cilium runs with kube-proxy replacement. I use Gateway API instead of an Ingress controller, and L2 announcement mode for the LoadBalancer pool.
Longhorn gives me replicated block storage across all three nodes. I didn’t want hostpath or local-path because there’s no replication, and Ceph seemed like it was going to be more complexity without any benefits for my use case. By not choosing Ceph, I thought this simplifying of the storage aspect of the cluster would allow me to have a greater abstraction and to think of PVs at a Kubernetes level without necessarily thinking about the underlying tool that enabled storage.
I’ve used Longhorn before in the past, and I don’t even remember setting it up this time because the process is so easy. I haven’t ran into any edge cases yet where I need anything more complicated. I don’t even know the last time I opened the Longhorn dashboard.
cert-manager issues Let’s Encrypt certificates. The Gateway API support needs config.enableGatewayAPI. That’s about it on this one. It issues certificates. Super simple, super useful.
ExternalDNS talks to Cloudflare and writes records for Gateway HTTPRoutes and annotated Services automatically. Chef’s kiss. I love how it solves such a simple problem that can be time consuimg and usually distracting from the objective at hand.
kube-prometheus-stack runs Prometheus and Grafana in the cluster. The dashboards live on the Debian server, however that may change soon. I used to have this all displayed on an old T.V. in my office with various other dashboards tiled nicely, but that T.V. burned out recently. It deserves its rest though, that thing was ancient at this point. Anyways, back to the cluster.
I also utilize vclusters on top when I want a fresh control plane for experiments. Currently I have two that have been less temporary than others: one is an agent sandbox, the other a benchmarking sandbox. The others come and go as I add/remove any vclusters along the way when I feel they are necessary.
These platform level services have been so dependable, I had to consult my internal documentation a lot to write this post, because I simply forgot all the details. I’m busy building things and testing new ideas, and the underlying platform just stays rock solid.
What worked, what didn’t
Most of the platform layer has been boring in the right way. The operational moments that actually stuck with me are the ones where something small cost me lots of troubleshooting time.
Tailscale system extension removal. I used to run Tailscale as a Talos system extension on the nodes, but the ext-tailscale service stalled on every boot. On February 28, 2026, I removed it through a rolling image upgrade and moved Tailscale responsibility entirely to the Debian gateway. The boot reliability problem went away. This may be solved now, but I haven’t gone through the trouble of looking into it further since then.
talos-worker-01 rebuild. On March 29, 2026, the Talos installer kept writing to the USB boot media instead of the internal SSD. Existing Talos partitions on the USB made the installer skip to phase 0, so it never touched the disk I wanted. I reformatted the USB with Ventoy, applied the config from clean maintenance mode, and got all three nodes onto v1.11.5 with factory extensions. This is an annoyance when sometimes the lenovo boot loader assigning the USB key to SDA, and sometimes the internal SDD to SDA. Again, I didn’t look into this one further. There’s probably another solution to this.
Stale KubePrism endpoint. During the Tailscale extension removal, I found a stale API endpoint (192.168.0.100:6443) listed on every node. It was left over from old DHCP days. It was pre-existing and not causing an active problem, but it was a good reminder to keep static-IP hygiene tight.
Subnet migration. On March 20, 2026, I moved the wired network from 192.168.0.0/24 to the current isolated 192.168.2.0/24. That meant updating Talos static-IP patches, machine configs, client configs, the Cilium LoadBalancer pool, and my docs all at once. It was tedious but straightforward. This also means that my home network traffic isn’t mixed in with my homelab traffic.
Single gateway risk. The Debian gateway is the only way in or out. The LUKS and UPS postmortems already document what happens when that single box has a bad day. I won’t retell them here; see the links at the end.
What I learned
- Talos is worth it if you want Kubernetes without weekend OS maintenance. That’s why I switched. Not ideal for those studying for the CKA, but I cleared that years ago and don’t care too much about the day-to-day maintenance of underlying OS and Kubernetes.
- A flat L2 network is fine until you assume it’s segmented. The cameras are on the same wire as the cluster. Not an issue in my case, but worth calling out.
- System extensions can fail silently on boot. If a node’s boot path depends on an extension, have a rollback plan that doesn’t require the extension to be healthy.