Docker Compose All The Things: Running 10+ Services on One Host

By Anas Semesmieh · April 20, 2026 · Homelab · Docker

My homelab runs entirely on Docker Compose — no Kubernetes, no Nomad, no Proxmox clustering. Just one Ubuntu Server, a dozen docker-compose.yml files, and a reverse proxy that ties it all together. Here's how each piece fits.

The Architecture

Each service lives in its own directory under ~/, with its own docker-compose.yml. This keeps services isolated, independently restartable, and easy to back up. The bring-up order matters: infrastructure first (Traefik, Pi-hole), then applications.

Infrastructure Layer

Traefik — Reverse Proxy + Local CA TLS

Traefik is the front door. It routes *.homelab domains to the right container and terminates TLS using a self-signed wildcard certificate from a local CA I generate with OpenSSL:

openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
  -subj "/CN=Homelab Local CA"

openssl genrsa -out wildcard.key 2048
openssl req -new -key wildcard.key -out wildcard.csr \
  -subj "/CN=*.homelab"

openssl x509 -req -in wildcard.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out wildcard.crt -days 3650 \
  -extfile <(printf "subjectAltName=DNS:*.homelab,DNS:homelab")

Every service gets HTTPS automatically. I trust the CA on my client devices so there are no browser warnings. Health check: curl -k https://traefik.homelab/ping.

Pi-hole — DNS + Ad Blocking

Pi-hole serves as the network's primary DNS. It resolves *.homelab to the host IP and blocks ads network-wide. My router's DHCP hands out the Pi-hole IP as the DNS server, so every device on the network benefits automatically.

Application Layer

Homepage — The Dashboard

Homepage is the landing page with live widgets pulling stats from every service: Traefik routing status, Pi-hole query counts, Plex active streams, Tautulli history, Portainer container counts, and Glances system metrics. The widget config lives in homepage/config/services.yaml, which references API keys for each service.

Plex + Tautulli — Media Server + Analytics

Plex serves my media library. Tautulli monitors who's watching what, tracks history, and provides Plex usage analytics. Both authenticate via tokens stored in their config files (redacted in the backup repo).

Arr Stack — Media Acquisition Pipeline

This is the most complex service group, running five containers via a single docker-compose.yml:

The Arr services communicate via Docker DNS (http://prowlarr:9696), and the WireGuard private key is injected at runtime (never stored in Git).

Immich — Photo Backup

Immich is a self-hosted Google Photos alternative, backed by PostgreSQL. The database password is generated on first install and stored in a local .env file that's excluded from version control. Photos sync automatically from my phone.

Home Assistant — Automation Hub

Home Assistant manages smart home devices. Its config directory is backed up, but runtime state and database are excluded. Long-lived access tokens are generated via the web UI and never committed.

Glances — System Metrics

Glances exposes system metrics on port 61208. Homepage's Glances widget pulls CPU, memory, network, and disk stats in real-time. No authentication required for internal access.

Bring-up Order

When rebuilding from scratch, order matters:

  1. Traefik — Reverse proxy must be up first for routing
  2. Pi-hole — DNS must be available for *.homelab resolution
  3. Homepage — Dashboard (can start without backends, widgets show errors until services come up)
  4. PlexTautulliArr StackImmichHome Assistant

What Makes This Work

The key insight: each service is a self-contained Compose project with its own docker-compose.yml. No monolithic stack file. This means I can restart, upgrade, or debug any service without touching the others.

Next up: Disaster Recovery in Under an Hour — how I rebuild this entire stack from a clean Ubuntu install.