DEV Community

Cover image for Building a Modern Kubernetes Homelab: GitOps with Flux CD, Terraform, and DevPod
Alan Varghese
Alan Varghese

Posted on

Building a Modern Kubernetes Homelab: GitOps with Flux CD, Terraform, and DevPod

If you have ever built a home server or local Kubernetes lab, you probably know the classic homelab lifecycle:

  1. You spend an entire weekend deploying clusters, setting up ingress, and spinning up self-hosted apps.
  2. Three months later, something breaks or you want to upgrade a component.
  3. You realize you ran 50 manual kubectl apply commands and edited configs directly on the node, leaving behind zero documentation.
  4. 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)
Enter fullscreen mode Exit fullscreen mode

Why this separation matters:

  • infrastructure/ vs apps/: 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"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Rancher Desktop runs on the host, exposing /var/run/docker.sock. When I run:

devpod up .
Enter fullscreen mode Exit fullscreen mode

DevPod launches a fully preconfigured development container. Inside this container:

  • docker commands communicate directly with Rancher Desktop's engine.
  • kubectl connects 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"
Enter fullscreen mode Exit fullscreen mode

☁️ 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

What happens behind the scenes?

  1. Flux generates its controller manifests and pushes them to your Git repo.
  2. Flux applies those manifests to the Kubernetes cluster.
  3. Flux configures deploy keys and starts polling the repository.
  4. 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:

  1. 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.
  2. Containerize Your Dev Environment: Tools like DevPod and DevContainers make switching machines or onboarding contributors seamless.
  3. 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)