A Practical Homelab: Self-Hosting and DevOps on a Small Cluster
If you’re reading this, you probably have a closet full of old servers, a fondness for automation, and a stubborn belief that the best tooling is the stuff you run yourself rather than the cloud. The flashy “one-click” stacks are seductive, but in a homelab they’re often brittle, expensive to scale, and hard to audit offline. The real superpower is boring reliability: predictable backups, sane security, gradual upgrades, and a workflow that doesn’t crash when your internet goes out for a weekend.
In this article I’ll lay out a practical, opinionated plan to build a self-hosted DevOps and automation stack in a modest homelab. It’s deliberately lean, easy to operate, and designed to grow as you prove the value of automation. You’ll come away with a workable Kubernetes-based stack, GitOps to keep everything in check, a lightweight CI/CD that runs offline if needed, and concrete steps you can copy-paste into your environment.
Hook: why you should care about boring, boring-strong automation
The bottleneck in most homelabs isn’t the feature set; it’s the day-to-day operations that eat your time. You want a stack that you can maintain without constant firefighting: gradual upgrades, predictable outages, clear rollback paths, and a single source of truth for your deployments. A boring, well-laid-out stack does that by design. It’s not sexy, but it’s the kind of thing you can rely on for years.
1) The core philosophy: boring, scalable, and observable
- Start with a small, reliable Kubernetes cluster (k3s) that you can run on 2–3 nodes. If you’re just starting, a single RM or a couple of beefy VMs will do; the goal is to prove out the pattern, not to pretend you’re replacing cloud providers.
- Use GitOps as your deployment engine. The cluster is managed by code in Git, and all changes flow through Argo CD (or Flux). This gives you a clear, auditable history and a sane rollback story.
- Add a lightweight CI/CD that runs pipelines inside the cluster and uses your own runners for sensitive operations.
- Use local storage or lightly managed storage (Longhorn) for persistent workloads, with backups to a safe external location (restic + Velero).
- Harden the stack layer-by-layer: TLS with cert-manager, secrets via sealed secrets, and defensive networking (firewalls, VPNs, least privilege).
2) The practical stack (what to install and why)
- Kubernetes layer: K3s (a slim, easy-to-operate Kubernetes distribution)
- Pros: small footprint, simple install, good for homelabs.
- Cons: a bit opinionated; keep your node count modest to avoid operational debt.
- Storage and networking
- Longhorn for dynamic volumes (easy to manage in K3s)
- MetalLB for load balancing on bare metal or private networks
- Ingress and TLS
- Traefik as the Ingress controller (or stick with the built-in Traefik that ships with K3s)
- cert-manager to automate TLS certificates (Let’s Encrypt in normal operation)
- GitOps and cluster management
- Argo CD (in-cluster) for GitOps
- Sealed Secrets (Bitnami) to protect Kubernetes secrets in Git
- CI/CD and automation
- Drone CI (self-hosted) or a lightweight alternative like a GitHub Runner if you prefer GitHub Actions flavor
- Optional: a small, self-hosted CI runner for sensitive steps
- Observability and backups
- Prometheus + Grafana for metrics
- Loki for logs (optional but nice)
- Velero for backups; Restic for file-level backups
- Identity and access (optional, but nice)
- Lightweight OAuth/OIDC proxy if you want SSO for Argo CD and dashboards
- Pi-hole for local DNS/ad-blocking (helps with privacy and home network hygiene)
3) High-level topology
- The cluster runs on 2–3 nodes (one as control plane, others as workers). If you’re starting small, a single server cluster that you intend to scale is fine.
- Ingress sits in front of services; Traefik handles TLS via cert-manager.
- Argo CD talks to Git repositories to apply manifests to the cluster.
- A separate backup path (Velero to an NFS share or object store) keeps data safe.
- CI/CD runs inside the cluster with an agent that executes pipelines against your own registry and environment.
4) Getting started: a concrete blueprint you can actually implement
Prerequisites
- A couple of nodes (bare metal or VMs) with Debian/Ubuntu LTS
- Static IPs or a predictable DHCP reservation
- A domain you control (for TLS, subdomains)
- Basic familiarity with kubectl and Manifests
Step 1: Prepare the cluster
- Install k3s on the first node (server)
- curl -sfL https://get.k3s.io | sh -
- Note the token: cat /var/lib/rancher/k3s/server/node-token
- Ensure you can reach the API: export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
- Install k3s agent on additional nodes
- On each worker: curl -sfL https://get.k3s.io | K3S_URL="https://<server-ip>:6443" K3S_TOKEN="<token>" sh -
- Confirm cluster is healthy
- kubectl get nodes
- kubectl get pods --all-namespaces
Step 2: Storage and networking
- Install Longhorn for dynamic storage
- kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.3.0/deploy/longhorn.yaml
- Wait for the UI to come up; ensure that default StorageClass is Longhorn
- Install MetalLB (layer-2) for service exposure if you’re on private networks
- apply manifests from the MetalLB project; configure a pool of IPs that you own
- Confirm you can create a PersistentVolumeClaim and get a PV
Step 3: Ingress and TLS
- If you’re using the built-in Traefik with K3s, verify it’s running
- kubectl -n kube-system get pods | grep traefik
- Install cert-manager
- kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml
- Wait for cert-manager to be ready
- Create an Issuer or ClusterIssuer for Let’s Encrypt
- Example manifest (ClusterIssuer) for Let’s Encrypt staging first, then production
- Apply and validate
- Enable a sample ingress with TLS (you’ll switch to your real domain)
- Use a simple app (e.g., whoami) to test
- Ensure TLS certificates are issued and renew automatically
Step 4: GitOps with Argo CD
- Install Argo CD
- kubectl create namespace argocd
- kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
- Expose the Argo CD API with a safe method (port-forward is fine for initial setup)
- kubectl port-forward svc/argocd-server -n argocd 8080:443
- Connect Argo CD to a Git repository
- Create a project, repository, and applications that point to your manifests
- Define a small sample application