A Practical, Self-Hosted DevOps Stack for Your Homelab
Two years ago I gutted my cloud bill by turning a tired old NAS and a couple of modest NUCs into a small, self-hosted DevOps playground. The goal wasn’t to replicate every cloud service, but to ship a reliable, repeatable workflow for building and deploying my projects without leaving the house. The result wasn’t glamorous—just a lean stack that actually works, with sane defaults and a clear upgrade path. If you’re reading this, you probably want the same: a pragmatic, maintainable pipeline you can trust, not a nine-figure blueprint you’ll never finish.
Here’s the practical path I followed, with concrete steps, tradeoffs, and small deviations you can adopt or ignore. The idea is to start small, learn what actually hurts, and then scale in measured, low-risk increments.
Start with a scope you can defend
The biggest trap in homelab DevOps is scope creep. You don’t need a giant, evergreen pipeline to start; you need a reliable one. My rule of thumb: at minimum, you should be able to answer yes to these:
- I have a self-hosted Git repository with pull requests and code reviews.
- I can run a CI job that builds and tests code, then publishes artifacts to a registry.
- I can deploy those artifacts to a reproducible environment (dev/stage/prod) from the same code repo.
- I have basic observability and backups so I don’t lose work or know when things go wrong.
Everything beyond that—advanced security tooling, AI-assisted dashboards, multi-region disaster recovery—can be layered on later. Start with Git, CI, Registry, and a simple deploy workflow, plus a basic monitor and a backup strategy. You’ll be surprised how far you can get with a couple of well-chosen components and a solid runbook.
Pick your host(s) and base OS
Hardware matters more than people admit. You don’t need a data-center-grade tank to start; you need predictable, accessible hardware, redundancy where it counts, and noise you can tolerate in a home environment.
- One or two modest nodes for a start (e.g., a NAS with a couple of fast disks and a small PC with 16 GB RAM). The idea is to keep critical control plane and worker workloads on dedicated hardware, separate from generic file services.
- Base OS: Ubuntu Server LTS or Debian. Both are stable, well-documented, and have good community support for all the projects you’ll run.
- Virtualization vs bare metal: virtualization makes upgrading easier. I run a lightweight virtualization layer (Proxmox or TrueNAS SCALE) so I can spin up VMs for Kubernetes nodes, CI runners, and registries without rebooting the host for every test.
The KISS path: a small Kubernetes cluster
Kubernetes is not required to start a self-hosted DevOps stack, but it makes scaling and upgrades painless. My preference is a small k3s cluster (single master, one or two workers) on two or three nodes. It’s substantially lighter than full Kubernetes and has a huge ecosystem of Helm charts and community support.
- Why k3s? It’s already opinionated about sane defaults, uses less memory, and is easy to bootstrap on a couple of VMs.
- Swap off, kernel settings sane, and cgroups enabled. These are the boring prerequisites that save you countless headaches later.
A pragmatic, mostly-works install path:
- Create two or three VM instances (or containers) with Ubuntu 18.04/20.04+.
- Install k3s on the first node: curl -sfL https://get.k3s.io | sh -
- Join additional nodes with the provided token: curl -sfL https://get.k3s.io | K3S_URL=https://<masterIP>:6443 K3S_TOKEN=<token> sh -
- Verify with kubectl get nodes; you should see your control plane and workers in a couple of minutes.
Self-hosted Git: Gitea as your single source of truth
The minimum requirement for “self-hosted” is a reliable Git system with a strong basic workflow. Gitea is underrated in its blend of simplicity, low footprint, and good enough features for hobby projects.
What to run:
- A Gitea instance on Kubernetes or a single VM, exposed via a simple Ingress. I prefer the Kubernetes route with a small Helm chart, but a Docker-compose approach on a single node also works well for a start.
- Repositories with a clear branch strategy (main for prod, develop for integration) and protected branches that require PR reviews.
A practical example using a Helm chart (high level):
- Add the Helm repo and install Gitea with sane defaults for resource limits and a persistent volume claim.
- Configure SSH and webhook callbacks to trigger your CI system.
- Enable basic user management so you can lock down access using teams.
Pros and caveats:
- Pros: Instant, centralized code history; easy PR workflows; simple access control.
- Caveats: Backups are crucial; you’ll want to back up the Gitea data and perhaps the database daily. Also, keep TLS offload simple—use a small Ingress with Let’s Encrypt for automation.
CI/CD: Drone CI over Kubernetes (or GitLab CE if you prefer)
Two common routes exist for self-hosted CI/CD in a homelab: Drone CI and GitLab CE. Both can run on Kubernetes, but Drone typically wins on small clusters due to its lean footprint and simpler runner model. The core strategy is to have CI that takes code from Gitea, runs tests, builds artifacts, and pushes images or packages to a registry.
Drone CI basics:
- Drone server and agents run in Kubernetes. The server handles UI, pipelines, and OAuth integration; agents execute builds.
- Integration with Gitea is via a dedicated OAuth app and a secret (client_id and client_secret) stored in Kubernetes as a secret.
- Pipelines are declared as .drone.yml in your repo, defining steps like test, build, and publish.
A concrete minimal pipeline example (drone.yml):
- name: ci
kind: pipeline
steps:
- name: test
image: golang:1.20
commands: [ go test ./... ]
- name: build
image: docker:24
commands: [ docker build -t gitea.example.local/myservice:${DRONE_COMMIT_SHA} . ]
- name: push
image: plugins/docker
settings:
repo: gitea.example.local/myservice
tags: latest, ${DRONE_COMMIT_SHA}
username:
password:
Artifacts handling:
- Push built images to a private registry, then deploy from there to your cluster. This keeps your pipeline independent from any external registry and gives you reproducible builds.
Alternatives and notes:
- If you’re more comfortable with GitLab CE, it’s an all-in-one solution that includes CI, issue tracking, and a container registry. It’s heavier, but for some teams, it’s simpler to manage.
Container registry: Harbor or a simple Docker Registry
If you’re building and deploying containers, you need a registry you control. Harbor is a good default choice: it adds role-based access control, image scanning, and LDAP/AD integration if you need it.
What to run:
- Harbor on Kubernetes with an Ingress and TLS. It’s feature-rich without being overwhelming.
- Tag and promote images across environments (dev → stage → prod) using a simple policy in your CI pipeline.
Basic security:
- Enable content trust and image scanning. Don’t deploy unsigned images into production-like environments.
- Use a separate namespace or project for each application, with limited permissions for CI users.
Ingress, TLS, and security basics
In a home environment, you’ll likely expose a few services to the internet. Do not punt on TLS and authentication.
- Ingress controller: Traefik is my default because of its dynamic configuration and native Let's Encrypt support; it's easy to set up and works well in Kubernetes.
- TLS: Use Let’s Encrypt certificates via the Ingress. Automate renewal and keep a short renewal window to minimize outages.
- MFA for admin access: Enable two-factor authentication for Gitea, Drone, and any admin interfaces. If you’re not using MFA, you’re inviting trouble.
- Network segmentation: Create a small internal network for your homelab services. If possible, place the CI/CD network behind a firewall rule that restricts inbound access to the minimum.
Observability: Prometheus, Grafana, Loki
A monitoring stack that doesn’t require a PhD to understand is priceless. The combination I find most practical is Prometheus for metrics, Grafana for dashboards, and Loki for logs.
- Prometheus: scrape metrics from your Kubernetes components, your CI runners, and your application microservices. Use a small, retention-focused storage with a reasonable scrape interval (5-15 seconds for apps, 60 seconds for system metrics).
- Grafana: build dashboards that give you quick visibility into build times, failure rates, and registry health. Start with a main pipeline dashboard and a node health dashboard.
- Loki: gather logs from containers and the host. Create a couple of simple queries to spot failing steps or flaky tests.
A sample observability workflow:
- Collect metrics from Drone, Gitea, and your apps.
- Dashboards highlight spike in build times or failed deployments.
- Alertmanager notifies you on failures, but keep alert fatigue in check; start with critical issues only (CI pipeline failures, registry downtime, and cluster health).
Backups and disaster recovery: Velero, snapshotting, and rclone
Homelab backups are more about data retention than “petabytes in the cloud.” Your priorities:
- Chief data: Gitea repositories, CI artifacts, and registry data.
- Cluster state: Kubernetes manifests, secret data (encrypted), and Helm release history.
Backup ideas:
- Velero for Kubernetes resource backups and persistent volume backups. Schedule nightly backups and test restores quarterly.
- Regular filesystem backups: back up the Gitea data directory and the Harbor registry data to an external drive or a NAS snapshot, plus send a copy to a cloud target if you have network egress.
- Rclone syncs to a remote storage bucket for off-site redundancy. Do not rely on a single on-site backup.
Automation and provisioning: Ansible for repeatability
To avoid drift, manage infrastructure with a repeatable playbook. Ansible helps you:
- Install and configure k3s, Gitea, Drone, Harbor, and the observability stack.
- Manage TLS certificates and DNS records.
- Apply cluster-wide configuration, such as resource quotas and RBAC.
A practical approach:
- Maintain a small Ansible repo with roles for bootstrap (OS hardening, Docker/Containerd, k3s), platform services (Gitea, Drone, Harbor), and observability (Prometheus, Grafana, Loki).
- Use a simple inventory with group_vars for different environments (homelab-dev, homelab-prod).
- Integrate Ansible with your CI: have the pipeline trigger on a versioned Ansible playbook to apply changes to the cluster in a predictable way.
A simple, real-world workflow:
- You make a code change in a repo.
- Drone runs tests, builds an artifact, and pushes a new image to Harbor.
- An Ansible playbook applies the new image tag to the dev environment, then, after validation, promotes it to staging and production with a controlled, documented process.
End-to-end project example: a small app pipeline
Take a tiny Go service stored in Gitea. The flow looks like this:
- Developer pushes a branch to Gitea; the PR is opened.
- Drone detects the PR, runs unit tests, and builds a Docker image with a version tag equal to the commit SHA.
- The image is pushed to Harbor, and a release is created via a Helm chart that deploys the service into the k3s cluster.
- The Ingress routes the app to users, while Grafana dashboards reveal deployment health and test results.
Concrete steps you can take this weekend
- Pick one node and get Gitea running with a basic repo. Get user access and a protected branch workflow in place.
- Add Drone CI with a minimal pipeline. Ensure a test phase, a build, and a simple container image push to Harbor.
- Deploy Harbor and push a sample image; wire its credentials into Drone, so you can promote from dev to prod.
- Add a basic Nginx/Traefik ingress route to the new app and configure TLS with Let’s Encrypt.
- Spin up a small Prometheus/Grafana/Loki stack and create a couple of dashboards showing build success rate and container health.
- Create a simple backup strategy for Gitea and Harbor data; schedule Velero or a basic rsync/rsync-like backup to an external drive.
Costs, maintenance, and common pitfalls
- Costs: There’s no free lunch; you trade cloud spend for hardware, power, and time. Start with existing hardware and avoid buying more unless you actually need it. Prioritize storage for artifacts and backups; CPU and RAM are your bottlenecks in the early days.
- Maintenance: The stack will require monthly housekeeping—updates, security patches, certificate renewals, and occasional reconfigurations as your services evolve.
- Pitfalls to avoid:
- Over-engineering early. Start with Git, CI, and a registry; add observability later.
- Skipping backups. A single disk failure will teach you the value of backups quickly.
- Underestimating security. Expose only what you need, and enable MFA for critical access.
Conclusion: a practical path forward in small, steady steps
The most powerful thing you can build in a homelab is discipline: a small, reliable pipeline that you can trust and extend. Start with a tight scope: Git hosting, CI for a single project, and a private registry. Once that foundation is solid, you can layer in automated deployments, a richer observability stack, and a robust backup strategy.
Actionable takeaway:
- Install a minimal k3s cluster and run Gitea and Drone on it.
- Add Harbor as your registry and wire it into Drone for artifact publishing.
- Deploy a minimal Prometheus/Grafana/Loki stack and create a single dashboard that shows CI health and deploy status.
- Document everything in a living runbook and commit it to your Gitea repository so your future self doesn’t have to relearn the wheel.
If you treat this as a three-week experiment rather than a weekend sprint, you’ll arrive at a practical, maintainable homelab DevOps stack you can expand without breaking your sanity. The goal isn’t perfection; it’s predictability, repeatability, and a few things you can actually trust when you need them most. Start small, stay disciplined, and you’ll thank your past self later.