Projects / active

homelab-docker

Every Docker stack in the lab, one self-contained folder each, validated in CI on every push.

  • Docker Compose
  • Gitea Actions
  • Renovate
  • restic

This is the repo that runs most of the services in my house. It’s a set of Docker Compose stacks with one strict rule: every top level folder is one project and has everything it needs to run. I wrote up the reasoning in One Compose project per directory.

What’s in it

Project What it does
gitea My Git server (local only, homelab network and VPN), Postgres and an Actions runner
gitea-runner-* Extra runners on the NAS and on freyja
backup Nightly restic backup of Gitea to Backblaze B2, deployed by CI
nginx Nginx Proxy Manager with Authelia in front for SSO
librechat A self hosted LibreChat AI stack
lancache Caches game downloads so the second install is instant
homelab The media automation stack, plus Homepage and Uptime Kuma
streaming Kavita for books and manga
minecraft A Fabric server for friends, behind a GeoIP firewall
monitoring node exporter and cAdvisor feeding the k3s Prometheus
airprint Makes my old Canon printer show up on phones
localsend An always on LocalSend inbox, installed natively
wireguard-ha WireGuard with keepalived failover

How it’s kept honest

  • Secrets never get committed. Every project has a .env.example with every key documented and fake values. The real .env is gitignored.
  • Only config is tracked. Databases, caches and world saves are all gitignored, so the repo is the blueprint and backups are the data.
  • CI on every push. A workflow on my own Gitea runners runs docker compose config on every stack, lints the YAML, and runs shellcheck on the scripts that CI deploys.
  • Renovate opens PRs when images update.
  • Docs live in the wiki. Each project’s README is a short pointer to its wiki page, because that’s the one I actually keep up to date.

The backup job

The one I care about most is backup. Every night at 03:30 it takes a restic snapshot of the whole Gitea stack and pushes it to Backblaze B2, encrypted on my side before it leaves. It’s deployed by CI, so the script that runs is always the script in the repo. More on why that matters in Running my own Git server.