Files
homelab-optimized/docs/DOCKER_COMPOSE_GUIDE.md
Gitea Mirror Bot c88a6b13b6
Some checks failed
Documentation / Build Docusaurus (push) Failing after 7s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-03-07 09:18:03 UTC
2026-03-07 09:18:03 +00:00

9.5 KiB

🐳 Docker Compose Guide

Comprehensive guide for Docker Compose usage in the homelab environment

📋 Overview

This guide covers Docker Compose best practices, patterns, and configurations used throughout the homelab infrastructure for consistent and maintainable container deployments.

🏗️ Standard Compose Structure

Basic Template

version: '3.8'

services:
  service-name:
    image: organization/image:latest
    container_name: service-name
    restart: unless-stopped
    
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Los_Angeles
    
    volumes:
      - ./config:/config
      - /data/service:/data
    
    ports:
      - "8080:8080"
    
    networks:
      - homelab
    
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service.rule=Host(`service.vish.gg`)"
      - "com.centurylinklabs.watchtower.enable=true"

networks:
  homelab:
    external: true

🔧 Configuration Patterns

Environment Variables

environment:
  # User/Group IDs (required for file permissions)
  - PUID=1000
  - PGID=1000
  
  # Timezone (consistent across all services)
  - TZ=America/Los_Angeles
  
  # Service-specific configuration
  - DATABASE_URL=postgresql://user:REDACTED_PASSWORD@db:5432/dbname
  - REDIS_URL=redis://redis:6379
  
  # Security settings
  - SECURE_SSL_REDIRECT=true
  - SESSION_COOKIE_SECURE=true

Volume Mapping

volumes:
  # Configuration (relative to compose file)
  - ./config:/config
  - ./data:/data
  
  # Shared storage (absolute paths)
  - /mnt/storage/media:/media:ro
  - /mnt/storage/downloads:/downloads
  
  # System integration
  - /var/run/docker.sock:/var/run/docker.sock:ro
  - /etc/localtime:/etc/localtime:ro

Network Configuration

networks:
  # External network (created separately)
  homelab:
    external: true
  
  # Internal network (service-specific)
  internal:
    driver: bridge
    internal: true

🏷️ Labeling Standards

Traefik Integration

labels:
  # Enable Traefik
  - "traefik.enable=true"
  
  # HTTP Router
  - "traefik.http.routers.service.rule=Host(`service.vish.gg`)"
  - "traefik.http.routers.service.entrypoints=websecure"
  - "traefik.http.routers.service.tls.certresolver=letsencrypt"
  
  # Service configuration
  - "traefik.http.services.service.loadbalancer.server.port=8080"
  
  # Middleware
  - "traefik.http.routers.service.middlewares=auth@file"

Watchtower Configuration

labels:
  # Enable automatic updates
  - "com.centurylinklabs.watchtower.enable=true"
  
  # Update schedule (optional)
  - "com.centurylinklabs.watchtower.schedule=0 0 4 * * *"
  
  # Notification settings
  - "com.centurylinklabs.watchtower.notification-url=ntfy://ntfy.vish.gg/watchtower"

Monitoring Labels

labels:
  # Prometheus monitoring
  - "prometheus.io/scrape=true"
  - "prometheus.io/port=9090"
  - "prometheus.io/path=/metrics"
  
  # Service metadata
  - "homelab.service.category=media"
  - "homelab.service.tier=production"
  - "homelab.service.owner=vish"

🔐 Security Best Practices

User and Permissions

# Always specify user/group IDs
environment:
  - PUID=1000
  - PGID=1000

# Or use user directive
user: "1000:1000"

# For root-required services, minimize privileges
security_opt:
  - no-new-privileges:true

Secrets Management

# Use Docker secrets
secrets:
  db_password:
    "REDACTED_PASSWORD" ./secrets/db_password.txt

services:
  app:
    secrets:
      - db_password
    environment:
      - DB_PASSWORD_FILE=/run/secrets/db_password

Network Security

# Avoid host networking
network_mode: host  # ❌ Avoid this

# Use custom networks instead
networks:
  - internal  # ✅ Preferred approach

# Limit exposed ports
ports:
  - "127.0.0.1:8080:8080"  # ✅ Bind to localhost only

📊 Resource Management

Resource Limits

services:
  service-name:
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 512M

Health Checks

services:
  service-name:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Restart Policies

# Standard restart policy
restart: unless-stopped

# Alternative policies
restart: "no"          # Never restart
restart: always        # Always restart
restart: on-failure    # Restart on failure only

🗂️ Multi-Service Patterns

Database Integration

version: '3.8'

services:
  app:
    image: myapp:latest
    depends_on:
      - database
    environment:
      - DATABASE_URL=postgresql://user:REDACTED_PASSWORD@database:5432/myapp
    networks:
      - internal

  database:
    image: postgres:15
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - internal
    secrets:
      - db_password

volumes:
  db_data:

networks:
  internal:
    driver: bridge

secrets:
  db_password:
    "REDACTED_PASSWORD" ./secrets/db_password.txt

Reverse Proxy Integration

services:
  app:
    image: myapp:latest
    networks:
      - homelab
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`app.vish.gg`)"
      - "traefik.http.routers.app.entrypoints=websecure"
      - "traefik.http.routers.app.tls.certresolver=letsencrypt"

networks:
  homelab:
    external: true

🔄 Development vs Production

Development Override

# docker-compose.override.yml
version: '3.8'

services:
  app:
    build: .
    volumes:
      - .:/app
    environment:
      - DEBUG=true
    ports:
      - "8080:8080"

Production Configuration

# docker-compose.prod.yml
version: '3.8'

services:
  app:
    image: myapp:v1.2.3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 1G
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

📝 Documentation Standards

Service Documentation

# At the top of each compose file
# Service: Application Name
# Purpose: Brief description of what this service does
# Access: How to access the service (URL, port, etc.)
# Dependencies: Other services this depends on
# Volumes: Important volume mappings
# Configuration: Key environment variables

Inline Comments

services:
  app:
    image: myapp:latest
    container_name: myapp
    restart: unless-stopped
    
    environment:
      # Required: User/group for file permissions
      - PUID=1000
      - PGID=1000
      
      # Optional: Custom configuration
      - CUSTOM_SETTING=value
    
    volumes:
      # Configuration directory
      - ./config:/config
      
      # Data storage (persistent)
      - app_data:/data
    
    ports:
      # Web interface
      - "8080:8080"

🚀 Deployment Strategies

GitOps Deployment

# Compose files are deployed via Portainer GitOps
# Repository: https://git.vish.gg/Vish/homelab.git
# Branch: main
# Automatic deployment on git push

Manual Deployment

# Deploy stack
docker-compose up -d

# Update stack
docker-compose pull
docker-compose up -d

# Remove stack
docker-compose down

Stack Management

# View running services
docker-compose ps

# View logs
docker-compose logs -f service-name

# Execute commands
docker-compose exec service-name bash

# Scale services
docker-compose up -d --scale worker=3

🔍 Troubleshooting

Common Issues

# Check service status
docker-compose ps

# View logs
docker-compose logs service-name

# Validate configuration
docker-compose config

# Check resource usage
docker stats

Debug Commands

# Inspect container
docker inspect container-name

# Check networks
docker network ls
docker network inspect network-name

# Volume inspection
docker volume ls
docker volume inspect volume-name

📊 Monitoring Integration

Prometheus Metrics

services:
  app:
    labels:
      - "prometheus.io/scrape=true"
      - "prometheus.io/port=9090"
      - "prometheus.io/path=/metrics"

Log Management

services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        labels: "service,environment"

🔧 Advanced Patterns

Init Containers

services:
  app:
    image: myapp:latest
    depends_on:
      init:
        condition: service_completed_successfully
  
  init:
    image: busybox
    command: ["sh", "-c", "echo 'Initialization complete'"]

Sidecar Containers

services:
  app:
    image: myapp:latest
    volumes:
      - shared_data:/data
  
  sidecar:
    image: nginx:alpine
    volumes:
      - shared_data:/usr/share/nginx/html:ro
    ports:
      - "80:80"

volumes:
  shared_data:

📚 Additional Resources

External Documentation

Internal Resources


Last Updated: February 24, 2026
Docker Compose Version: 3.8+ recommended
Status: PRODUCTION - Used across all homelab services