A Practical Homelab: Self-Hosting CI/CD with Gitea, Drone, and a Lightweight Kubernetes

A Practical Homelab: Self-Hosting CI/CD with Gitea, Drone, and a Lightweight Kubernetes

Hook: If you’re tired of cloud egress fees, flaky runners, and flaky CI caching, your best move is to bring the CI/CD stack home—and make it boringly reliable. A well-built homelab isn’t a toy; it’s a focused, repeatable platform that you actually understand end-to-end. This is not a “stack of random docker-compose files.” It’s a curated, opinionated setup that emphasizes reproducibility, predictable costs, and practical observability.

What you’ll build (in plain terms)

  • A self-hosted Git server you actually own and control (Gitea) for versioning, pull requests, issues, and a single source of truth for configs and pipelines.
  • A lightweight CI/CD runner ecosystem that runs on your own hardware (Drone CI + Drone runners) to build, test, and package code without depending on remote clouds.
  • A small Kubernetes (k3s) cluster on your homelab to host apps, with GitOps-style deployment to real environments, not just “dev rigs.”
  • A lean, reliable monitoring/logging stack (Prometheus, Grafana, Loki) so you can actually see what’s going on.
  • A sane backup and disaster-recovery plan (BorgBackup for data, Offsite/RSYNC for critical artifacts, regular snapshots).
  • Strong security basics (TLS, VPN or WireGuard, firewall, and least privilege).

Before you start: what you’ll need

  • Hardware: at least two modest servers or two reliable laptops. Think 8–16 GB RAM per node for a minimal cluster; 32 GB is nicer if you’re running on multiple services. One machine can host the cluster control plane; the other acts as a data/service node and backup.
  • Network: a stable home network with a decent router, port-forwarding for external access, and ideally a dynamic DNS name for your cluster.
  • Power: a UPS for each machine; homelabs drift into “if it’s up for 24 hours, you’ll be happier.”
  • Storage: a fast SSD for the control plane and a larger HDD/SSD pool for artifacts and logs; consider ZFS or btrfs for snapshots if you’re comfortable with them.
  • OS: a modern Linux distro (Ubuntu LTS or Debian) with a sane security baseline. I’d pick Ubuntu Server 22.04+ or Debian 12+.

Part 1: Architecture decisions you should actually believe in

  • Do not “dabble” with CI in a dozen different containers on one host. Separate concerns: Git server, CI server, and apps in their own layers. This matters for recoverability.
  • Prefer a small Kubernetes footprint rather than a giant monolith. k3s is a great choice for homelabs. It gives you namespace isolation, declarative manifests, and a familiar way to deploy apps.
  • Version everything. Treat your infrastructure as code. If you can’t describe it in a repo, you’re reinventing the wheel each time you install something.
  • Prioritize reproducibility over “one-off” scripts. If you reset a machine, you should be able to bring it back to a known state with one command.

Part 2: Core components and why they fit a homelab

  • Gitea (Git hosting): Lightweight, self-contained, easy to back up. Serves as your code repository, issue tracker, and a single source of truth for your pipelines and infrastructure-as-code. It’s cheaper and easier to manage than GitHub Enterprise, and you’re not shipping all your secrets elsewhere.
  • Drone CI (self-hosted CI): A simple, container-based CI/CD that plays nicely with Git hosting (Gitea works well). It has a straightforward runner architecture: a server, and one or more runners that execute builds in containers. It scales with your hardware and keeps your builds fast by letting you pin runners to specific hardware or OS images.
  • k3s (lightweight Kubernetes): If you’re already into containerized deployments, a tiny Kubernetes cluster lets you run ArgoCD or Flux for GitOps, plus your apps, with real namespace boundaries. You’ll get mutability, declarative configuration, and replayability, which is worth the learning curve.
  • Prometheus + Grafana + Loki (observability): You can’t operate what you can’t observe. Prometheus scrapes metrics; Loki handles logs; Grafana renders dashboards. This trio is compact and scalable enough for a homelab while still being production-grade.
  • BorgBackup / Restic (backup): You need a plan for backups that isn’t “I’ll try to remember to copy later.” BorgBackup or Restic with a remote store or NAS gives you deduplicated, fast restores – exactly what you’ll need after a failure or an upgrade.
  • TLS & security basics: A reverse proxy (Traefik or Nginx) with Let’s Encrypt for TLS. A WireGuard VPN for remote access. Simple firewall rules (nfTables/ufw) that block everything by default and open only what’s needed.

Part 3: The rollout plan (practical, not mythical)

Week 1: Foundations

  • Stand up the hardware, install the OS, set up a basic firewall, and get a dynamic DNS name.
  • Enforce a consistent user and sudo policy. Create a single admin user and a non-root user with limited sudo privileges.
  • Prepare storage: format disks with a sane layout; enable LUKS if you care about disk encryption in case of theft; set up ZFS or Btrfs if you want snapshots and data integrity at the filesystem level.

Week 2: Core services

  • Deploy Gitea on a small VM or container on the control host. Use persistent volumes for /data. Enable backups for the repo data.
  • Deploy Drone CI server and a runner. Start with sqlite for the database during testing; then migrate to PostgreSQL for production workloads. Create at least one repository and configure a sample pipeline.
  • Install k3s on one or two nodes and join the cluster. Debounce upgrades; keep the cluster small until you’re confident.
  • Install a simple reverse proxy (Traefik) with TLS using Let’s Encrypt. Point your domain to the reverse proxy for the Drone server and Gitea.

Week 3: Observability and security

  • Deploy Prometheus and Grafana. Set up basic dashboards for CPU, memory, disk, and network usage. Add a few alert rules (high CPU, low disk space, node unreachable).
  • Add Loki for logs and a basic Grafana Loki data source. Create a simple log dashboard for Drone pipelines and cluster events.
  • Implement a WireGuard or OpenVPN-based VPN for remote access. Lock down admin interfaces to trusted networks only.
  • Harden the OS (fail2ban, SSH hardening, non-root login for SSH keys, disable password login).

Week 4: GitOps and automation

  • Install ArgoCD or Flux on the cluster. Tie it to a “desired state” repo (your Git repo with app manifests).
  • Create a small sample application manifest (a simple Nginx app) and drive deployments through GitOps. Simulate a faulty rollout and fix it from Git.
  • Create an Ansible playbook or a set of Terraform modules to provision new nodes, install Drone runners, and configure Gitea backups. The goal: you can re-create your environment from code.

Part 4: A concrete stack you can copy (high-level)

  • Hardware: 2 machines (e.g., 16 GB RAM each if possible; 32 GB is better). One acts as the control plane; the other for workloads and storage.
  • OS: Ubuntu Server 22.04 LTS.
  • Networking: Static or reserved IPs; domain example: homelab.local for internal; dynamic DNS for external access.
  • Core services:
  • Gitea running in a Docker container or a VM, with /data backed up weekly.
  • Drone server and one or more drone-runner-docker containers; configure a private registry if you want to store artifacts.
  • k3s cluster with 1–2 workers; install ArgoCD or Flux for GitOps.
  • Prometheus, Grafana, and Loki for observability; alert rules tuned to your environment.
  • BorgBackup/Restic configured to back up critical data to a NAS or remote server.
  • Traefik (or Nginx) with TLS certificates; WireGuard for remote access.

Part 5: A practical workflow example (how it actually works)

  • Repos and pipelines
  • You push to Gitea. A webhook notifies Drone. Drone decides what to do: run the pipeline for that repo, using the runner you configured.
  • The pipeline runs: compile, tests, lint, build Docker image (if applicable), push to your own registry, and store artifacts in a local artifact store.
  • A separate GitOps repo holds your Kubernetes manifests. When you push changes to that repo, ArgoCD/Flux automatically applies the changes to the cluster.
  • Local vs remote runners
  • Use local runners for fast feedback on small changes. Use dedicated runners for heavier tasks (builds that require more CPU or memory) to avoid starving the main system.
  • Pin runners to specific nodes, so you don’t have “noisy neighbors” affecting your main apps.
  • Pipeline examples (snippets)
  • A typical Drone pipeline (conceptual):
  • image: golang:1.20
  • steps:
  • name: test

commands:

  • go test ./...
  • name: build

commands:

  • go build -o app
  • name: package

commands:

  • docker build -t your-registry.local/app:${CI_COMMIT_REF} .
  • docker push your-registry.local/app:${CI_COMMIT_REF}
  • A minimal ArgoCD manifest (simplified) to auto-sync a demo app:
  • apiVersion: argoproj.io/v1alpha1
  • kind: Application
  • metadata: ...
  • spec:

project: default

source:

repoURL: 'https://gitea.local/your-org/your-app-config'

path: 'deploy'

targetRevision: HEAD

destination:

server: 'https://k3s.local'

namespace: default

syncPolicy:

automated:

prune: true

selfHeal: true

Part 6: Practical tips from real-world homelab experience

  • Start lean, iterate fast: It’s easy to over-engineer. Put in place a minimal, working baseline (Gitea + Drone + k3s) and add complexity only when you truly need it.
  • Use Infrastructure as Code from day one: If you can’t describe your environment as code, you’ll forget the steps. A small Ansible playbook to install the basics or a Terraform module to provision a VM makes disaster recovery trivial.
  • Use real backups and test restores: It’s not enough to have backups; you must verify you can restore. Schedule a test restore every quarter and keep the test data isolated.
  • Narrow your blast radius: Separate workloads into namespaces or projects. If a pipeline fails, it shouldn’t take down your cluster or other apps.
  • Embrace declarative configurations: Helm charts or Kustomize can help manage Kubernetes resources as code, not ad-hoc changes.
  • Make upgrades boring: Use a staged upgrade plan. Test new Drone or Gitea versions in a sandbox before upgrading production. Document upgrade steps in a changelog.
  • Prioritize network reliability: The biggest enemy of a home dev stack is a flaky network. If possible, maintain a separate, reliable link (a second ISP or at least a wired backhaul) for CI components.

Part 7: Common gotchas and how to avoid them

  • Resource constraints: If you run multiple services on a single node, you’ll quickly hit CPU/memory limits. Use resource requests/limits in Kubernetes and consider dedicating a node for CI and registry workloads.
  • Data fragmentation: Don’t spread data across too many volumes without a plan. Centralize persistence where appropriate and ensure backups cover all critical paths.
  • Security drift: It’s easy to open up ports for convenience. Apply the principle of least privilege; close access by default; audit who can push to pipelines and deploy to production.
  • TLS lifecycle: Automating TLS renewals avoids a sudden outage. Use Let’s Encrypt with automated renewal in Traefik or Nginx. Test renewal failures in a staging environment.

Actionable conclusion

  • Start with a minimal, boring baseline: Gitea for repos, Drone for CI with a single runner, and k3s for a tiny cluster. Add Prometheus, Grafana, and Loki as you gain confidence.
  • Build it with infrastructure-as-code first. If you can’t describe it in code, you don’t understand it well enough to maintain it.
  • Stabilize backups and disaster recovery before scaling. A restored environment is more valuable than a fancy feature.
  • Treat CI/CD as a product you own, not a hobby you tinker with on weekends. Make pipelines fast, reliable, and auditable.
  • Iterate every fortnight. Each cycle should deliver a small feature, a performance improvement, or a security hardening; never let the system drift into chaos.

If you want a concrete starter repo, I’d suggest a single Git repo containing:

  • Gitea configuration and data backup scripts
  • Drone configuration (server + runner) and an example pipeline
  • A minimal k3s cluster bootstrap (a Kubernetes manifest set)
  • A basic Prometheus/Grafana/Loki setup
  • A simple Ansible playbook to reproduce the environment on new hardware

This plan is not a magic bullet; it’s a practical, hard-won approach to turning a homelab into a dependable, self-hosted DevOps playground. You’ll learn a ton by building it, but the payoff is in the reliability and control you gain when you actually run real pipelines against real code in your own environment.