A 3-node K3s cluster running on Raspberry Pis at home. I use it to learn Kubernetes the hard way — by running real workloads on it, breaking things, and writing postmortems when I do.
The cluster is managed declaratively through GitOps: this repo is the source of truth, and FluxCD reconciles the cluster toward it. Everything you'd change with kubectl lives here instead.
What this demonstrates: Production-grade CI/CD pipeline design — from code commit to deployed application, with automated quality gates at every stage.
What to evaluate:
.github/workflows/— GitHub Actions pipeline definitionskubernetes/— Kustomize overlays for environment-specific deployment.devcontainer/— Standardized developer environmentsrc/— Application code (Python 3.13)release-please-config.json— Automated semantic versioning
Skills demonstrated: GitHub Actions, Trivy, PyTest, Ruff, Release Please, DevContainers, K3d, Kustomize, Docker, Python, zero-downtime deployment patterns
Time invested: 47+ commits
- K3s on three Raspberry Pis (one server, two agents), all on Wi-Fi
- FluxCD for GitOps reconciliation, with SOPS + Age for encrypted secrets in Git
- MetalLB (Layer 2) for
LoadBalancerservices - Traefik as ingress, with Cloudflare Tunnel for external access
- Longhorn for persistent storage
- Prometheus, Grafana, Alertmanager for monitoring
- Renovate for automated dependency updates
- Workloads: Uptime Kuma, Linkding, Grafana
apps/ # application workloads (base + staging overlays)
clusters/staging/ # Flux bootstrap manifests for the cluster
infrastructure/ # MetalLB, Traefik, Longhorn, cert-manager, etc.
monitoring/ # Prometheus / Grafana / Alertmanager
journal/ # runbooks and operational notes
postmortem-*.md # incident postmortems (the interesting bits)
- Push a commit to
main. - Flux's
source-controllernotices the new revision. kustomize-controllerbuilds the kustomization, decrypts any SOPS-encrypted secrets, and diffs against the live cluster.- Changes are applied. If reconciliation fails, the previous state is preserved and the failure surfaces in
flux get kustomizations.
No kubectl apply from a laptop, ever. If it isn't in Git, it isn't in the cluster.
The most useful artifacts in this repo. Each one is a real outage I debugged on this cluster:
- MetalLB VIP unreachable — Wi-Fi promiscuous mode — All ingress went dark. MetalLB logs said "announced." ARP failed everywhere.
tcpdumpaccidentally fixed it, which turned out to be the diagnostic: Wi-Fi firmware filters incoming ARP for IPs the card doesn't own, so MetalLB's raw socket never saw the requests. Permanent fix is a DaemonSet that keepswlan0in promiscuous mode. - Flux reconciliation failure — missing sops-age secret & stuck Terminating namespaces — Post-bootstrap, five kustomizations wouldn't reconcile. SOPS key was missing (it's not in Git, by design) and two namespaces were stuck Terminating because Longhorn's cluster-wide admission webhooks outlived its namespace, blocking finalizer cleanup.
- uptime-kuma VIP unreachable — Three overlapping causes: k3s's built-in
klipper-lbwas still running alongside MetalLB, MetalLB's memberlist port 7946 was blocked because Ubuntu 24.04 ships dual iptables backends and rules went to the wrong one, and a staleServiceL2StatusCR was stuck in a reconciliation loop on an immutable field.
It's a homelab. The control plane is a single node, storage is Longhorn on SD cards, and the nodes talk over Wi-Fi. A real production cluster would have HA control plane, wired networking (ideally with BGP-mode MetalLB), proper persistent storage, secret management backed by a KMS, and a CI pipeline gating PRs. The point of this project is to operate something end-to-end, not to pretend it's enterprise infrastructure.
flux bootstrap github \
--owner=bmacharia \
--repository=pi-cluster \
--branch=main \
--path=./clusters/staging
Then create the SOPS key secret (this step is intentionally outside Git):
kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey=./age.agekey
Maintainer: Babu Macharia · linkedin.com/in/babu-macharia · babumacharia.com