Writing

Building a homelab kubernetes cluster

The origin story behind my scrappy homelab Kubernetes cluster.

In this article series

3 articles
  1. 01 Building a homelab kubernetes cluster Current
  2. 02 A self-tuning homelab
  3. 03 How I access the services I self-host in my cluster

I have self-hosted things before, but none of it really stuck. A Raspberry Pi 4 ran Pi-hole for a while, then an old laptop ran Plex for a while after that. Both were useful in the loose way side projects are useful. Every now and then, something would break, I would usually shrug, things fizzle out and I would just move on.

This time round, I wanted to start something more serious. Something small, but with plenty of room to grow in whatever direction I felt like taking it, a testing ground for all sorts of learning experiments. I’d also GitOps it, so if I added a service, changed storage, or touched DNS, I wanted that change to live somewhere more durable than a vague memory of what I typed into a terminal. Nothing fancy, no rack, no enterprise gear. Just a small cluster for the services I actually use, with enough Kubernetes underneath it for me to learn something while running it.

I came up with the name rangoonpulse for the project because Rangoon is my hometown, and I liked giving the cluster a name that pointed back there. It made the project feel a little less like a random pile of YAML and a little more like something that belonged to me.

The starting point

After watching the secondhand market for a while, I come across a Lenovo m720q miniPC for SGD 200. That was the point where the plan stopped being a vague “one day” thing.

I already had a Raspberry Pi 4 sitting around, and I already had a QNAP T6 NAS on the network that I primarily use for photography RAW files and bulk storage. The miniPC tok the main role, running as both control plane and worker for now, with the plan to add more nodes later and split those roles out properly. The Pi slotted in beside it as a small utility node, and the NAS handled media and the large persistent volumes.

Spec wise, The m720q miniPC is the only piece of hardware worth listing in any detail: Intel i5-8400T (6c/6t), 32GB RAM, NVMe, and an Intel iGPU that ended up really handy for Jellyfin transcoding.

I also wanted to be honest with myself about budget from the start. The plan was to use what I already had, or pick things up cheap when I had to buy something, and keep recurring spend close to zero.

Lenovo ThinkCentre m720q mini PC in the homelab cabinet
The Lenovo ThinkCentre m720q that became the primary node.

Why Kubernetes and Talos?

In terms of what to run, Proxmox would have been the obvious answer. A couple of VMs, Docker inside them, snapshots when I need them, and a web UI for the boring bits. Hell, Docker Compose would have been even simpler. For five or six apps, a compose.yaml with Caddy out front would have done the job before dinner.

I picked Kubernetes because I wanted more hands-on time with the whole thing. At work I touch Kubernetes often enough to look at pods, check events and logs, tweak HPA or KEDA settings, and reason about how services behave in a cluster. That is still different from owning the whole thing end to end. Running it at home means I will also have to think about things like: ingress, DNS, certs, secrets, storage, monitoring, and rollouts. There’s a lot to learn here, and I’m looking forward to a building a deeper, more concrete understanding of how all these pieces fit together.

Kubernetes also gives me room to grow without changing the whole model. The cluster started with one mini PC and a Pi, but if I add another node later, Kubernetes already has a language for that. With a single Docker host, I know myself well enough to know I would eventually start inventing a worse version of this anyway.

That said, minimalism still mattered. My compute is small, and the last thing I wanted was Proxmox, VMs, guest OSes, and Docker all stacked on top of a tiny box that already does not have much to give. That’s why I went with Talos Linux instead of something like k3s. k3s itself is lean, but it still needs a general-purpose host OS underneath. Talos setup seems like it’s lean all the way down. It’s API-driven, immutable, and runs nothing it doesn’t need to.

GitOps from day one

Everything lives in Git and reconciled by Flux, with HelmRelease manifests as the unit of change. Secrets are encrypted with SOPS and age.

For most apps, I use the bjw-s app-template helm chart. It standardizes the repetitive parts: container, service, ingress, environment variables, and PVCs. I don’t need to write the same boilerplate twenty different ways: app-template provides the frame, and the app-specific details stay in values.yaml. When a dedicated chart fits better, I use it.

Over-engineered on purpose

Things might be a tad over-engineered for a homelab cluster. But it is also kinda the point.

GitOps with Flux means every change to the cluster starts in Git.. No ad-hoc manual kubectl apply. Every manifest, every HelmRelease, every Kustomize overlay lives in a repo, gets committed and gets reconciled automatically. If the cluster state drifts, Flux brings it back. If I broke something or I want to roll back, I git revert. The cluster state is, in effect, a git log.

Secrets are managed and encrypted through SOPS. Values are encrypted at rest in the repo with age keys, committed safely alongside everything else, and decrypted at apply time by the cluster. That includes things like VPN keys, API tokens, service passwords, TLS private keys … all of it goes through the same pipeline.

For monitoring and observability, I have Prometheus and Grafana which gives me dashboards for pretty much everything that is happening in the cluster. I can see at a glance whether a node is under pressure or whether a particular workload is consuming more resources than expected. I also have Uptime checks and some minimal alerts through Uptime Kuma and Alertmanager

What runs here

As for, what I am self-hosting,

Media takes up the most space: Jellyfin for the streaming, Seerr for requests, and the usual *arr stack, Sonarr, Radarr, Prowlarr, Bazarr, Autobrr, Profilarr plus a download client or two. The miniPC handles transcoding, so everything plays without hiccups. There is Immich for photos, AdGuard for DNS adblocks and Tunarr for IPTV.

I have a few personal tools like Vaultwarden for passwords, Actual Budget for money management. For books, I am still deciding between Calibre, Calibre Web Automated, Booklore and Audiobookshelf.

Other than these staple services, I am always testing out new services so this list is always evolving.

Apps, arr stack services, and ops tools running in the rangoonpulse Kubernetes cluster
The services currently running in the cluster.

Storage and persistence

The NAS was already part of my home setup before Kubernetes entered the picture. I use it for photography RAW files and bulk storage, so it made sense to let the homelab lean on it too.

Most app and config PVCs live on the NAS over NFS, mainly because it is the easiest path to grow. For databases and anything latency-sensitive, I keep local-path volumes on the miniPC’s NVMe.

QNAP NAS used for homelab storage
The QNAP NAS that backs most persistent app storage over NFS.

For now

So, this is where rangoonpulse is at for now. It will definitely keep changing. That’s the point.