← All articles
CONTAINERS Essential Docker Compose Stacks for Your Home Lab 2026-02-09 · docker · docker-compose · containers

Essential Docker Compose Stacks for Your Home Lab

Containers 2026-02-09 docker docker-compose containers self-hosted homelab

Docker Compose turns the chaos of docker run commands into clean, version-controlled YAML files. Instead of remembering a dozen flags and volume mounts, you define everything in a docker-compose.yml and bring it up with one command. For a home lab, this is the difference between a fragile mess and a reproducible setup.

This guide provides ready-to-use Docker Compose stacks for the services that most home labs benefit from. Each example is tested, production-ready, and includes the environment variables you'll actually need to change.

Before You Start

Create a directory structure to keep things organized:

mkdir -p ~/docker/{traefik,portainer,homepage,pihole,uptime-kuma,grafana,nextcloud,jellyfin,vaultwarden,paperless,immich}

Each service gets its own directory with its own docker-compose.yml. This keeps things modular — you can start, stop, and update services independently.

Make sure Docker and Docker Compose are installed:

# Install Docker (if not already installed)
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER

# Docker Compose v2 is included with Docker Engine
docker compose version

1. Traefik — Reverse Proxy

Traefik automatically discovers your Docker containers and routes traffic to them by hostname. It handles SSL certificates via Let's Encrypt, so every service gets HTTPS automatically.

# ~/docker/traefik/docker-compose.yml
services:
  traefik:
    image: traefik:v3.2
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config/traefik.yml:/etc/traefik/traefik.yml
      - ./config/acme.json:/acme.json
    networks:
      - proxy

networks:
  proxy:
    name: proxy
    external: true
# ~/docker/traefik/config/traefik.yml
api:
  dashboard: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false

certificatesResolvers:
  letsencrypt:
    acme:
      email: your-email@example.com
      storage: /acme.json
      httpChallenge:
        entryPoint: web
# Create the shared network and empty acme.json
docker network create proxy
touch ~/docker/traefik/config/acme.json
chmod 600 ~/docker/traefik/config/acme.json

Once Traefik is running, other containers just need labels to be reachable by hostname:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.myservice.rule=Host(`myservice.lab.example.com`)"
  - "traefik.http.routers.myservice.tls.certresolver=letsencrypt"

2. Portainer — Container Management UI

A web-based dashboard for managing Docker containers, images, volumes, and networks. Useful when you want to quickly check logs or restart a container without SSH-ing into the server.

# ~/docker/portainer/docker-compose.yml
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    ports:
      - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data

volumes:
  portainer_data:

3. Homepage — Dashboard

A fast, customizable dashboard that shows all your services in one place. Supports widgets for dozens of services with live status and statistics.

# ~/docker/homepage/docker-compose.yml
services:
  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - ./config:/app/config
      - /var/run/docker.sock:/var/run/docker.sock:ro

Configure services in ~/docker/homepage/config/services.yaml. Homepage auto-discovers Docker containers and displays their status.

4. Pi-hole — Network Ad Blocking

Blocks ads and trackers at the DNS level for your entire network. Set it as your network's DNS server, and every device benefits without needing browser extensions.

# ~/docker/pihole/docker-compose.yml
services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80"
    environment:
      TZ: "America/New_York"
      WEBPASSWORD: "change-this-password"
      FTLCONF_dns_listeningMode: "all"
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d

After starting Pi-hole, point your router's DNS settings to your server's IP address. Every device on the network will use Pi-hole automatically.

5. Uptime Kuma — Monitoring

A beautiful, self-hosted monitoring tool. Monitors HTTP endpoints, TCP ports, DNS, and more. Sends alerts via email, Slack, Discord, Telegram, or dozens of other services.

# ~/docker/uptime-kuma/docker-compose.yml
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "3001:3001"
    volumes:
      - ./data:/app/data

6. Grafana + Prometheus — Metrics and Dashboards

The standard monitoring stack. Prometheus scrapes metrics from your servers and services. Grafana visualizes them with beautiful dashboards. Add node_exporter to each machine you want to monitor.

# ~/docker/grafana/docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.retention.time=30d"

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3002:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: "change-this-password"
    volumes:
      - grafana_data:/var/lib/grafana

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - "--path.procfs=/host/proc"
      - "--path.sysfs=/host/sys"
      - "--path.rootfs=/rootfs"

volumes:
  prometheus_data:
  grafana_data:
# ~/docker/grafana/prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

  - job_name: "node"
    static_configs:
      - targets: ["node-exporter:9100"]

Import Grafana dashboard ID 1860 (Node Exporter Full) for a comprehensive server monitoring dashboard out of the box.

7. Nextcloud — File Sync and Collaboration

Self-hosted alternative to Google Drive/Dropbox. File sync, calendar, contacts, collaborative editing, and hundreds of apps.

# ~/docker/nextcloud/docker-compose.yml
services:
  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud
    restart: unless-stopped
    ports:
      - "8081:80"
    environment:
      MYSQL_HOST: nextcloud-db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: change-this-db-password
      NEXTCLOUD_ADMIN_USER: admin
      NEXTCLOUD_ADMIN_PASSWORD: change-this-admin-password
      NEXTCLOUD_TRUSTED_DOMAINS: "nextcloud.lab.example.com"
    volumes:
      - nextcloud_data:/var/www/html
    depends_on:
      - nextcloud-db

  nextcloud-db:
    image: mariadb:11
    container_name: nextcloud-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: change-this-root-password
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: change-this-db-password
    volumes:
      - nextcloud_db:/var/lib/mysql

volumes:
  nextcloud_data:
  nextcloud_db:

8. Jellyfin — Media Server

Stream your movies, TV shows, and music to any device. No subscriptions, no account required, no tracking. Supports hardware transcoding with Intel Quick Sync.

# ~/docker/jellyfin/docker-compose.yml
services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    ports:
      - "8096:8096"
    environment:
      JELLYFIN_PublishedServerUrl: "http://192.168.1.100"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /path/to/media/movies:/data/movies
      - /path/to/media/shows:/data/shows
      - /path/to/media/music:/data/music
    devices:
      - /dev/dri:/dev/dri  # Intel Quick Sync hardware transcoding

9. Vaultwarden — Password Manager

A lightweight, self-hosted Bitwarden-compatible password manager. Uses the official Bitwarden clients and browser extensions, but the server runs on minimal resources.

# ~/docker/vaultwarden/docker-compose.yml
services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    ports:
      - "8082:80"
    environment:
      DOMAIN: "https://vault.lab.example.com"
      SIGNUPS_ALLOWED: "false"
      ADMIN_TOKEN: "generate-a-long-random-token-here"
      SMTP_HOST: "smtp.example.com"
      SMTP_FROM: "vault@example.com"
      SMTP_PORT: 587
      SMTP_SECURITY: "starttls"
      SMTP_USERNAME: "your-smtp-user"
      SMTP_PASSWORD: "your-smtp-password"
    volumes:
      - ./data:/data

Set SIGNUPS_ALLOWED to true initially to create your account, then change it to false and restart the container. Access the admin panel at /admin using your ADMIN_TOKEN.

10. Paperless-ngx — Document Management

Scans, OCRs, and organizes your documents. Consume PDFs from a watched folder (or email), and Paperless automatically classifies, tags, and makes them searchable.

# ~/docker/paperless/docker-compose.yml
services:
  paperless:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    container_name: paperless
    restart: unless-stopped
    ports:
      - "8083:8000"
    environment:
      PAPERLESS_REDIS: redis://paperless-redis:6379
      PAPERLESS_DBHOST: paperless-db
      PAPERLESS_ADMIN_USER: admin
      PAPERLESS_ADMIN_PASSWORD: change-this-password
      PAPERLESS_OCR_LANGUAGE: eng
      PAPERLESS_TIME_ZONE: America/New_York
    volumes:
      - ./data:/usr/src/paperless/data
      - ./media:/usr/src/paperless/media
      - ./consume:/usr/src/paperless/consume
    depends_on:
      - paperless-db
      - paperless-redis

  paperless-db:
    image: postgres:16
    container_name: paperless-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: change-this-db-password
    volumes:
      - paperless_pgdata:/var/lib/postgresql/data

  paperless-redis:
    image: redis:7
    container_name: paperless-redis
    restart: unless-stopped

volumes:
  paperless_pgdata:

Drop PDFs into the consume folder, and Paperless will automatically import, OCR, and organize them.

11. Immich — Photo Management

Self-hosted alternative to Google Photos. Automatic backup from mobile devices, facial recognition, map view, timeline browsing, and AI-powered search.

# ~/docker/immich/docker-compose.yml
services:
  immich:
    image: ghcr.io/immich-app/immich-server:release
    container_name: immich
    restart: unless-stopped
    ports:
      - "2283:2283"
    environment:
      DB_HOSTNAME: immich-db
      DB_USERNAME: immich
      DB_PASSWORD: change-this-db-password
      DB_DATABASE_NAME: immich
      REDIS_HOSTNAME: immich-redis
    volumes:
      - ./upload:/usr/src/app/upload
    depends_on:
      - immich-db
      - immich-redis

  immich-machine-learning:
    image: ghcr.io/immich-app/immich-machine-learning:release
    container_name: immich-ml
    restart: unless-stopped
    volumes:
      - immich_ml_cache:/cache

  immich-redis:
    image: redis:7
    container_name: immich-redis
    restart: unless-stopped

  immich-db:
    image: tensorchord/pgvecto-rs:pg16-v0.2.0
    container_name: immich-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: immich
      POSTGRES_USER: immich
      POSTGRES_PASSWORD: change-this-db-password
    volumes:
      - immich_pgdata:/var/lib/postgresql/data

volumes:
  immich_ml_cache:
  immich_pgdata:

Tips for Managing Your Stacks

Start and Stop Services

# Start a service
cd ~/docker/jellyfin && docker compose up -d

# Stop a service
cd ~/docker/jellyfin && docker compose down

# View logs
docker compose logs -f --tail 50

# Update a service to the latest image
docker compose pull && docker compose up -d

Back Up Your Data

The most important thing to back up is the volumes and bind-mounted directories. The container images themselves can always be re-pulled.

# Simple backup script
#!/bin/bash
BACKUP_DIR="/path/to/backups/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"

for service in traefik portainer pihole uptime-kuma grafana vaultwarden paperless; do
  cd ~/docker/$service
  docker compose stop
  tar czf "$BACKUP_DIR/$service.tar.gz" .
  docker compose up -d
done

Keep Things Updated

# Update all services at once
for dir in ~/docker/*/; do
  echo "Updating $(basename $dir)..."
  cd "$dir" && docker compose pull && docker compose up -d
done

# Clean up old images
docker image prune -af

These eleven services cover the core needs of most home labs: traffic routing, monitoring, file management, media, security, and document organization. Start with a few that match your immediate needs, and add more as you grow into them. The beauty of Docker Compose is that each service is independent — you can add, remove, or rebuild any service without affecting the others.