Skip to content
aRustyDev's Blog
Go back

Building a Homelab Kubernetes Cluster

Edit on GitHub

Running Kubernetes at home started as a way to learn the platform without cloud bills. Three iterations later, it’s become genuinely useful for self-hosting services and testing deployments before they hit production.

Hardware Evolution

Iteration 1: Raspberry Pi 4 cluster (3 nodes)

Iteration 2: Used Dell Optiplex micro PCs

Iteration 3 (current): Mixed cluster

Network Architecture

The network setup went through similar iterations. What worked:

ISP Router (bridge mode)
    |
pfSense Box (routing, firewall, DNS)
    |
Managed Switch (VLANs)
    |
├── VLAN 10: Management (iDRAC, switch mgmt)
├── VLAN 20: Kubernetes nodes
├── VLAN 30: Services (exposed to home network)
└── VLAN 40: IoT isolation

Key decisions:

Storage

Storage in homelab k8s is always the hard part. Options I’ve tried:

For databases needing fast storage, I use local NVMe with nodeAffinity to pin pods. Not cloud-native, but it works.

What’s Running

Current workloads:

Lessons Learned

Start simple: My first attempt had Ceph, Istio, and a dozen operators. It was impossible to debug. Strip it down to essentials first.

GitOps from day one: ArgoCD or Flux. Managing a cluster with kubectl apply quickly becomes chaos.

Backup your etcd: Lost a cluster once because I didn’t. Never again.

Monitoring isn’t optional: You can’t fix what you can’t see. Prometheus + Grafana is the minimum.

Power matters: When your cluster draws 200W 24/7, the electricity bill adds up. Efficient hardware pays for itself.

Is It Worth It?

For learning, absolutely. Nothing teaches you Kubernetes like running it yourself and fixing it when it breaks at 2 AM.

For practical self-hosting, it depends. A single Docker host with Compose is simpler for most use cases. Kubernetes makes sense when you actually need its features - rolling updates, scaling, multi-node redundancy.

I’d do it again, but I’d skip the Pi cluster phase.


Edit on GitHub
Share this post on:

Previous Post
eBPF for Observability: A Practical Introduction
Next Post
Rust Error Handling: Beyond Result and Option