All posts
Home LabDevOpsSelf-HostedTerraform

Running a GitOps Home Lab: Infrastructure as a Weekend Hobby

My home server runs 15+ self-hosted services managed entirely via Git commits. Here's the architecture, the stack, and why I think everyone in tech should have a home lab.

I've been running a self-hosted home lab for a few years now, and it's become my favourite way to stay sharp on infrastructure outside of work. The setup has evolved from a single Raspberry Pi running Pi-hole into a proper GitOps-driven node running 15+ services — all managed via Git commits.

Here's how it works and why I think more engineers should have one.

The stack

The node is an amd64 machine running DietPi. Everything runs in Docker Compose, and every stack is committed to a private Git repository called localstack.

Networking:

  • Traefik — reverse proxy with automatic TLS via Let's Encrypt DNS challenge
  • Tailscale — mesh VPN for private service access across all my devices
  • Cloudflare Tunnels — zero-port-forward public exposure for selected services
  • OAuth2-proxy — Google OAuth gate in front of every public service

Storage & data:

  • PostgreSQL — shared database for Immich and Vaultwarden
  • Redis — cache for Immich
  • Kopia — encrypted, deduplicated off-site backups

Self-hosted services:

  • Immich — Google Photos replacement with ML-powered search
  • Jellyfin — media server for local video and music
  • Vaultwarden — self-hosted Bitwarden-compatible password manager
  • Syncthing — continuous file sync across devices
  • ESPHome — firmware for home automation sensors
  • Excalidraw, Rustpad, Stirling PDF — productivity tools

The GitOps agent

The piece I'm most proud of is the custom GitOps agent. It's a small service that runs on the node and polls the Git repository on a schedule. When it detects a new commit, it pulls the latest state and applies any changed Docker Compose stacks with docker compose up -d.

This means deploying a new service or changing a configuration is as simple as:

git commit -m "add rustpad stack"
git push

The agent picks it up within minutes and applies the change. No SSH sessions, no manual docker compose commands. It's the same GitOps loop I use professionally, but running at home lab scale.

Infrastructure as Code with Terraform

DNS records, Cloudflare Tunnel ingress rules, and Zero Trust access policies are all managed in Terraform. This has two benefits:

  1. Everything is version controlled. If I accidentally delete a DNS record, git log tells me what it was and terraform apply puts it back.
  2. Drift is impossible. Terraform's plan/apply cycle keeps the declared state and actual state in sync.

The Cloudflare provider handles creating tunnel routes — so adding a new public service is a one-line Terraform change and a git push.

Why bother?

Running a home lab keeps certain skills alive that are easy to lose if your daily work is purely product-focused:

  • Debugging without a safety net. When something breaks at home, there's no on-call team. You figure it out.
  • Infrastructure intuition. Understanding how networking, TLS, DNS, and container orchestration fit together makes you a better engineer at any level.
  • Real ownership. Every architectural decision is yours, and you live with the consequences. That sharpens judgment quickly.

Beyond the skills angle, there's something deeply satisfying about hosting your own data. My photos live on my hardware, managed by software I understand and can inspect.

The most important thing I've learned

Start simple and grow incrementally. My first iteration was a single docker-compose.yml file with three services and no automation. The GitOps agent, Terraform, and multi-stack architecture came later, one problem at a time.

If you're starting out: get one service running on one machine. Then add the second. The automation follows naturally once you feel the pain of manual deployments.