Production homelab: 50+ services on 3 nodes

Three Ubuntu boxes. One Caddy reverse proxy. Fifty-plus self-hosted services running the show — email, git, CI/CD, dashboards, password manager, photo backup, and a small Kubernetes cluster for the things that need it.

The stack

NodeRoleSpecs
edge-01Reverse proxy + firewall2 vCPU, 4GB RAM, public IP
app-01Application workloads8 vCPU, 32GB RAM, NVMe
data-01Databases + storage8 vCPU, 32GB RAM, 2×4TB HDD (RAID1)

What runs where

edge-01 (public-facing):

  • Caddy with auto HTTPS via Cloudflare DNS-01
  • Crowdsec + fail2ban for intrusion detection
  • WireGuard for admin access

app-01 (workloads):

  • Woodpecker CI
  • Gitea (lightweight self-hosted Git)
  • Drone runners (3 instances)
  • Plausible Analytics
  • Uptime Kuma
  • Healthchecks.io (cron monitoring)

data-01 (stateful):

  • PostgreSQL 16 (single primary, streaming replication to app-01)
  • Redis 7
  • MinIO (S3-compatible, for backups + media)
  • Restic repo (nightly snapshots → B2 + local)

Orchestration

Single docker-compose.yml per service, kept in a private Gitea repo. Deploys via a small bash script that:

  1. Pulls latest config from Gitea
  2. Runs docker compose pull
  3. Health-checks before stopping the old container
  4. Rolls back automatically if health check fails

Zero-downtime deploys for everything that’s stateless.

Monitoring

  • Prometheus on data-01, scraping all 3 nodes via node-exporter
  • Grafana dashboards for CPU, RAM, disk, network, Docker containers
  • Uptime Kuma for external service health
  • Alertmanager → Telegram when anything’s off

Backups

  • Restic → Backblaze B2 (encrypted, deduplicated)
  • Hourly snapshots of databases, daily of everything else
  • 30-day retention + monthly archive
  • Monthly restore drill (yes, I actually test)

What’s next

  • Replace single PostgreSQL with Patroni + etcd (HA)
  • Move stateless workloads to a small k3s cluster
  • Add Ceph for proper distributed storage

Full config and dashboards: github.com/nuteo/homelab (placeholder)