If you have ever built a home server or local Kubernetes lab, you probably know the classic homelab lifecycle:
- You spend an entire weekend deploying clusters, setting up ingress, and spinning up self-hosted apps.
- Three months later, something breaks or you want to upgrade a component.
- You realize you ran 50 manual
kubectl applycommands and edited configs directly on the node, leaving behind zero documentation. - You tear everything down and start from scratch.
To break that cycle, I decided to treat my homelab like a production-grade platform engineering project: completely declarative, automated with Infrastructure as Code (IaC), synchronized via GitOps, and portable across machines using containerized developer environments.
Here is how I designed and architected my Kubernetes homelab setup using Rancher Desktop, DevPod, Terraform, and Flux CD.
🛠 The Architecture & Tech Stack
The goal of this setup is simple: Zero Click-Ops, zero snowflake machines, and 100% reproducibility.
| Layer | Tool | Role in the Lab |
|---|---|---|
| Local Host Runtime | Rancher Desktop | Lightweight local Kubernetes (k3s) and container engine exposing /var/run/docker.sock. |
| Cloud Infrastructure | Terraform | Provisions cloud infrastructure (Azure AKS, networking, storage) declaratively. |
| Dev Environment | DevPod + Mise | Instant, reproducible dev container mounting the host's Docker socket and managing CLI tools. |
| GitOps Delivery | Flux CD | Reconciles cluster state against Git manifests automatically. |
| Workload Packaging | Kustomize & Helm | Multi-environment config management (base, staging, production). |
🏗 Repository Layout: Designing for Scale
When building with GitOps, how you structure your Git repository dictates how easy it is to maintain and troubleshoot. Here is the layout of the project:
.
├── .devcontainer/ # DevPod container spec (Dockerfile, devcontainer.json)
├── Azure-infrastructure-provisioning-terraform/ # Terraform IaC for Azure resources
├── apps/ # Application manifests
│ ├── base/ # Common Kustomize manifests
│ ├── staging/ # Staging overlays
│ └── production/ # Production overlays
├── clusters/ # Flux CD root configurations
│ ├── staging/ # Staging cluster entrypoint
│ └── production/ # Production cluster entrypoint
├── infrastructure/ # Cluster-wide infrastructure services
│ ├── configs/ # Namespaces, certs, ingress rules
│ └── controllers/ # Cert-manager, ingress-nginx, external-dns
├── monitoring/ # Observability stack
│ ├── configs/ # Dashboards, PrometheusRules, scrape configs
│ └── controllers/ # kube-prometheus-stack, Loki, Promtail
├── docs/ # Architecture notes and documentation
└── mise.toml # Deterministic CLI versions (helm, gh, uv)
Why this separation matters:
-
infrastructure/vsapps/: Core cluster dependencies (ingress, cert-manager) are isolated from tenant applications. If an app breaks, your ingress controller remains untouched. -
clusters/entrypoints: Flux monitors a specific cluster folder (e.g.,clusters/production). From there, it points to infrastructure and app modules. -
Azure-infrastructure-provisioning-terraform/: Keeps cloud provisioning separated from Kubernetes runtime manifests.
⚡ The Developer Experience: DevPod + Rancher Desktop
One common pain point in Kubernetes homelabbing is keeping host toolchains in sync across laptops and desktops (kubectl, helm, flux, terraform, etc.).
Instead of polluting my host machine, I use DevPod backed by Rancher Desktop.
The Secret Sauce: Docker Socket Passthrough
In .devcontainer/devcontainer.json, I mount the host's Docker socket:
{
"build": {
"context": "..",
"dockerfile": "Dockerfile"
},
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
]
}
Rancher Desktop runs on the host, exposing /var/run/docker.sock. When I run:
devpod up .
DevPod launches a fully preconfigured development container. Inside this container:
-
dockercommands communicate directly with Rancher Desktop's engine. -
kubectlconnects directly to the local cluster. - All dependencies are isolated from my host OS.
On top of that, Mise manages tool versions defined cleanly in a mise.toml file:
[tools]
docker-cli = "latest"
github-cli = "latest"
helm = "latest"
uv = "latest"
☁️ Cloud Parity with Terraform
While Rancher Desktop provides a low-cost, quiet local sandbox, real-world DevOps often requires cloud capabilities (public IPs, DNS automation, cloud storage).
Inside Azure-infrastructure-provisioning-terraform/, Terraform provisions the cloud footprint:
cd Azure-infrastructure-provisioning-terraform
terraform init
terraform plan
terraform apply
This establishes consistent network policies, storage accounts for remote state, and managed Kubernetes instances that can be toggled on and off without incurring continuous cloud costs.
🔄 Bootstrapping GitOps with Flux CD
Once the cluster is reachable, the only manual command executed is the Flux bootstrap.
This is the bridge that transitions the cluster from an empty canvas to an autonomous, Git-reconciled platform:
flux bootstrap github \
--owner=<your-github-username> \
--repository=HOMELAB \
--branch=main \
--path=clusters/production \
--personal
What happens behind the scenes?
- Flux generates its controller manifests and pushes them to your Git repo.
- Flux applies those manifests to the Kubernetes cluster.
- Flux configures deploy keys and starts polling the repository.
- From this point forward, any file added or edited in Git is automatically reconciled in the cluster within seconds.
No more kubectl apply -f deployment.yaml from your terminal. If it isn't in Git, it doesn't exist.
💡 Key Takeaways for Building Your Own Homelab
If you are planning to build or rebuild your Kubernetes homelab, here are three principles that will save you countless headaches:
- Avoid Click-Ops Early: Resist the temptation to run ad-hoc commands to "just test something out." Define it as a Kustomize overlay or a Helm release in Git right away.
- Containerize Your Dev Environment: Tools like DevPod and DevContainers make switching machines or onboarding contributors seamless.
- Separate Concerns: Keep Cloud IaC (Terraform), cluster controllers (Flux/cert-manager), and application workloads in distinct layers.
🚀 What's Next?
With the foundational pipeline and GitOps workflow in place, my next milestones include:
- [ ] Setting up automated TLS certificate management with cert-manager and Cloudflare DNS.
- [ ] Deploying the observability stack with Prometheus, Grafana, and Loki.
- [ ] Implementing secret management via SOPS / Age encryption directly in the Git repository.
What does your homelab tech stack look like? Are you team Flux CD or team Argo CD? Let me know in the comments below!
Top comments (0)