Build a practical self-hosted homelab: from bare metal to automated DevOps in your living room
Homelabs aren’t about showing off gear; they’re about failing fast, automating the boring parts, and learning the tradecraft you’ll actually use at work. A well-tuned self-hosted stack lets you test CI pipelines, deploy services, and experiment with security in a controlled, repeatable way—without risking production. This article is my compact, opinionated playbook: what to buy, how to wire it up, and the small, concrete routines I use to keep a home lab productive, resilient, and boringly reliable.
Hook: you’ll save time, reduce anxiety about upgrades, and actually ship code on weekends. The trick is to start small, standardize aggressively, and automate everything you can push into a single coherent workflow.
1) Why a homelab matters for DevOps and automation
If you’re doing DevOps in the real world, you’re constantly debugging pipelines, testing infrastructure changes, and wrestling with security hardening. A homelab is your living room sandbox where you can practice the exact same patterns you’ll deploy at work: versioned infrastructure, immutable deployments, and observability-rich environments. The payoff isn’t flashy dashboards; it’s confidence. You can push small changes to a staging-like environment, see exactly how they behave, and roll back with one command when needed.
Key benefits I rely on:
- Reproducible environments: everything is defined as code, and you can rebuild a node in minutes.
- Safer experiments: you can test new CI runners, new storage backends, or new security controls without affecting production.
- End-to-end automation: you ship code, run tests, build artifacts, and deploy to a staging cluster—mirroring real pipelines.
- Long-term cost efficiency: you own the stack, you learn the trade-offs, and you avoid the “cloud bill surprise” trap on small experiments.
2) A pragmatic topology for a home-lab
I favor a lean, modular stack that stays under one roof without demanding enterprise-scale infrastructure. Here’s a practical topology that works for most homes with a spare PC or two:
- Hypervisor layer: Proxmox VE (free, mature, simple VM/LXC management)
- Core services (VMs or LT containers):
- DNS, DHCP, and Pi-hole/AdGuard for network-wide ad-blocking and name resolution
- VPN gateway (WireGuard) to securely reach the lab from outside
- Storage/NFS for shared data (optional but helpful)
- Kubernetes cluster (K3s) for running apps, pipelines, and services
- GitOps and CI/CD: a Git-hosted workflow with self-hosted runners or runners-as-a-service in the cluster
- Monitoring and logging: Prometheus + Grafana + Loki (and alerting via Alertmanager)
- Backup and recovery: snapshotting (VM-level) and off-site backups to object storage
Why this mix? It’s a practical balance between control, cost, and learning. Proxmox gives you clean VM isolation and easy backups. K3s is tiny, friendly, and production-like enough for pipelines and apps. Flux or ArgoCD delivers a real GitOps loop, so your cluster state is a reflection of your Git repo. Monitoring is accessible today and scales with you.
3) Hardware and network baseline
You don’t need a monster rack to start. Here’s a sane baseline that covers most common home setups:
- Host machine: a repurposed PC or a small server with:
- 16–32 GB RAM (more if you plan heavy workloads or lots of containers)
- 2–4 TB fast SSDs for OS and container images
- A second drive for backups/storage if budget allows
- Networking:
- A router that supports WireGuard or at least 1–2 VLANs for isolation
- A stable home IP or dynamic DNS for remote access
- Power and cooling:
- A small UPS to survive power blips
- Adequate ventilation; homelab hardware loves airflow
The rule I follow: buy once, buy enough to avoid bottlenecks for two years. Don’t chase absolute minimums for the sake of saving a few dollars; you’ll thank yourself when builds aren’t swapping memory to swap and dashboards stay responsive.
4) Core stack: from Proxmox to a Kubernetes cluster
Step 1: Proxmox as the base
- Install Proxmox VE on the host(s). Set up a management network and an isolated storage pool if you can.
- Create VMs for the components you need. I run:
- A small VM as a DNS/VPN gateway
- A storage VM (NFS or Ceph if you’re feeling fancy)
- A Kubernetes control plane VM (or two for high availability if you’re ambitious)
- A few worker nodes (1–2 is workable for a home lab)
- For storage, ZFS on Proxmox is a solid choice if you want snapshots and easy backups.
Step 2: Kubernetes with K3s
- K3s is ideal for homelabs: lightweight, easy to install, and still featureful.
- Install on each node with a simple bootstrap:
- On the first node: curl -sfL https://get.k3s.io | sh -s - --cluster-init
- On additional nodes: curl -sfL https://get.k3s.io | K3S_URL=https://<server>:6443 K3S_TOKEN=<token> sh -s - --server
- Use the built-in service account credentials to join the cluster. You’ll set up a single control plane with optional extra components.
What to deploy on day one:
- Core DNS (CoreDNS is usually included)
- Ingress controller (traefik or NGINX)
- A dedicated namespace for gitops and pipelines
A minimal K3s cluster is enough to start testing pipelines, Helm charts, and a few microservices.
Step 3: GitOps and CI/CD
- GitOps: Flux CD or Argo CD are mature options. I prefer Flux for its simplicity with git workflows.
- Self-hosted runners: You can run a runner inside the cluster (e.g., a Linux worker) or stand up a dedicated VM for CI tasks. If you’re integrating with GitHub, you’ll typically register a GitHub Actions runner on Linux and pin it to a standard label (self-hosted, lab-runner).
- Example flow:
- Commit to Git: a Helm chart change or a Kubernetes manifest
- Flux/ArgoCD detects drift, applies the changes to the cluster
- The pipeline builds artifacts, publishes to a registry, and triggers deployment
5) Basic automation patterns you’ll actually use
- Declarative infrastructure:
- Use Terraform or a local Pulumi project to model your cluster, DNS, and VPN configuration
- Keep cluster-side manifests in Git and pair them with Flux/ArgoCD for GitOps
- Automated provisioning of new services:
- A small Ansible/Helm-based workflow to add a service in one place
- A Helm chart for common services (nginx-ingress, cert-manager, monitoring stack), with values.yaml to customize per-service deployments
- Secrets management:
- Do not store raw secrets in Git. Use Kubernetes Secrets (with encryption at rest) and a secret store (e.g., Sealed Secrets, or external vault if you’re feeling fancy)
- Continuous delivery on a home-lab scale:
- A typical flow: code pushed -> CI builds artifacts -> artifact pushed to a registry -> GitOps deploys to the cluster
- Observability as code:
- Prometheus rules as code, Grafana dashboards versioned, and alert routing defined in a YAML file so you can audit it like any other config
6) Concrete examples you can copy (or adapt)
Code block 1: Minimal cloud-init for a new VM in Proxmox
#cloud-config
users:
- name: admin
lock-passwd: false
passwd: $6$rounds=4096$abcdefghijkl$abcdefghijklmnopqrstuvwx
ssh-authorized-keys:
- ssh-ed25519 AAAA...
packages:
- curl
- git
- vim
- dnsutils
runcmd:
- [ sh, -c, 'apt-get update && apt-get upgrade -y' ]
- [ sh, -c, 'systemctl enable --now ssh' ]
Code block 2: K3s bootstrap (single node, then join others)
On first node
curl -sfL https://get.k3s.io | sh -s - --cluster-init
On a second node (join)
TOKEN=$(awk '/token/ {print $2}' /var/lib/rancher/k3s/server/token)
curl -sfL https://get.k3s.io | K3S_URL=https://<first-node-ip>:6443 K3S_TOKEN=$TOKEN sh -
Code block 3: A simple Kubernetes manifest to deploy a static app
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello
image: hashicorp/http-echo:0.2.3
args: ["-text=Hello from the home-lab!"]
ports:
- containerPort: 5678
Code block 4: Ingress route example (Traefik)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: hello.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-world
port:
number: 5678
Code block 5: Flux bootstrap (GitOps)
Install Flux
curl https://fluxcd.io/install.sh | bash
Bootstrap (replace placeholders)
flux bootstrap github \
--owner=<your-github-username> \
--repository=<lab-repo> \
--branch=main \
--path=clusters/home-lab
Code block 6: Prometheus/Grafana stack (Helm)
Add repos
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
Install a simple stack
helm install prometheus prometheus-community/kube-prometheus-stack
Access Grafana via port-forward during initial setup
kubectl port-forward svc/prometheus-grafana 3000:80
Code block 7: GitHub Actions runner (self-hosted)
On a dedicated VM
sudo useradd -m -d /home/runner -s /bin/bash runner
cd /home/runner
mkdir actions-runner
cd actions-runner
curl -o actions-runner-linux-x64-2.0.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.0.0/actions-runner-linux-x64-2.0.0.tar.gz
tar xzf actions-runner-linux-x64-2.0.0.tar.gz
./config.sh --url https://github.com/<org>/<repo> --token <token> --labels home-lab-runner --unattended
sudo ./svc.sh install
sudo ./svc.sh start
7) Security, hardening, and backups you can rely on
- Zero-exposure baseline: keep the VPN as the sole entry point from outside; do not expose Kubernetes API or management dashboards directly to the internet without a VPN or strong auth front door.
- TLS all the things: Let’s Encrypt certificates for ingress. Automate certificate renewal with cert-manager. Ensure your DNS records are protected with DNSSEC if possible.
- Secrets management: keep secrets out of Git. Use sealed secrets or a KMS-backed secret store. Rotate credentials on a schedule and whenever someone leaves the project.
- Backups:
- VM snapshots in Proxmox (weekly or after major changes)
- Offsite backups to object storage (S3-compatible) for critical data (e.g., database dumps, config repos)
- Periodic test restores to verify you can recover
- Hardening basics:
- Disable password-based SSH login; use SSH keys and a bastion for management
- Keep the system updated; automate patching where feasible
- Implement role-based access control (RBAC) for Kubernetes and Git tools
8) Common gotchas and how to avoid them
- Performance surprises: a single 4-core CPU may bottleneck tightly-packed clusters and CI workloads. Monitor memory and CPU usage; if you see swapping or frequent OOM events, scale the cluster or tighten workloads.
- Configuration drift: GitOps is your friend, but you still need disciplined processes. Treat the cluster like production: code in, drift out. Commit policies help.
- Backup drift: test restores regularly. A backup you cannot restore is a liability.
- Networking hazards: VPN misconfigurations are easy to overlook. Validate NAT rules, firewall settings, and DNS resolution in a closed environment before exposing services.
- Documentation debt: keep a single-source-of-truth repo with architecture diagrams, deployment instructions, and recovery playbooks. It saves you hours when something breaks midweek.
9) A short, actionable plan to start now
- Week 0: Audit your current hardware. Identify a target host, install Proxmox VE, and create two VMs: one for a DNS/VPN gateway and one for a small Kubernetes control plane.
- Week 1: Build a K3s cluster (1 control plane, 1 worker). Verify kubectl access and basic networking. Deploy a hello-world app behind an ingress.
- Week 2: Add a GitOps loop with Flux. Put the initial manifests into a Git repo and bootstrap Flux to sync the cluster.
- Week 3: Introduce a CI runner in a dedicated VM or in-cluster. Create a simple pipeline that builds a container image and updates a Helm release.
- Week 4+: Expand monitoring and logs. Install Prometheus and Grafana, add basic dashboards, and set up alert rules for high CPU/memory usage and pod restarts. Begin implementing a backup plan and test restores.
10) Final thoughts and a pragmatic mindset
A home-lab should be a reliable amplifier for your professional practice, not a weekly fire drill. Keep your stack as small as possible but as capable as you need. Favor declarative configurations over ad-hoc scripts. Favor consistent, automated backups over heroic manual recoveries. And above all, ship small, frequently. The best validation of your learning is not a perfect diagram, but a pipeline that quietly builds, tests, and deploys exactly what you expect.
Actionable conclusion
- Start with a modest Proxmox-based setup: 1 control-plane VM, 1 worker VM, plus a DNS/VPN gateway. Keep the rest for future expansion.
- Publish your lab under a GitOps pattern using Flux or ArgoCD. Version your cluster configuration in Git and let the cluster converge automatically.
- Add a simple CI runner and a minimal pipeline that builds a container image, stores it in a local registry, and deploys a Helm chart to your cluster.
- Deploy Prometheus/Grafana and set up meaningful, automated alerts. Ensure you have backup and disaster recovery tested at least quarterly.
- Iterate: add one new service at a time, standardize its deployment with Helm, and keep all changes in Git. Your future self will thank you for the discipline.
With this approach, your homelab becomes a practical, repeatable engine for DevOps learning and real-world testing, not a mystery box of gear that sits idle most of the time. Build, automate, observe, and recover—the core habits that separate hobby setups from useful, production-like environments you’ll actually rely on at work.