Files
homelab-optimized/DOCKER_COMPOSE_GUIDE.md
Gitea Mirror Bot 1f0c60f4db
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m3s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-16 07:36:59 UTC
2026-04-16 07:36:59 +00:00

8.0 KiB

🐳 Docker Compose Guide

Comprehensive guide for Docker Compose best practices in the homelab

Overview

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

File Structure Standards

Naming Conventions

  • Service files: service-name.yml or service-name.yaml
  • Stack names: Use descriptive, kebab-case names
  • Container names: Include service and host identifier
  • Volume names: Prefix with service name for clarity

Directory Organization

host-name/
├── service-name/
│   ├── docker-compose.yml
│   ├── .env
│   ├── config/
│   └── data/
└── service-name.yml (simple services)

Compose File Best Practices

Version and Services

version: '3.8'  # Use stable version

services:
  service-name:
    image: official/image:tag  # Always pin versions
    container_name: service-name-hostname
    restart: unless-stopped   # Standard restart policy

Environment Variables

# Prefer environment files
env_file:
  - .env

# Or explicit environment variables
environment:
  - PUID=1000
  - PGID=1000
  - TZ=America/New_York

Volume Management

volumes:
  # Named volumes for data persistence
  - service-data:/app/data
  
  # Bind mounts for configuration
  - ./config:/app/config:ro
  
  # Host paths for media/large data
  - /mnt/storage/media:/media:ro

volumes:
  service-data:
    driver: local

Network Configuration

networks:
  default:
    name: service-network
    
  # Or use existing networks
  proxy:
    external: true
    name: nginx-proxy-manager_default

Security Best Practices

User and Permissions

services:
  app:
    user: "1000:1000"  # Run as non-root user
    
    # Or use environment variables
    environment:
      - PUID=1000
      - PGID=1000

Resource Limits

services:
  app:
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
        reservations:
          memory: 256M

Security Options

services:
  app:
    security_opt:
      - no-new-privileges:true
    
    # Read-only root filesystem when possible
    read_only: true
    tmpfs:
      - /tmp
      - /var/tmp

Common Patterns

Reverse Proxy Integration

services:
  app:
    labels:
      # Nginx Proxy Manager
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`app.domain.com`)"
      
      # Or Traefik labels
      - "traefik.http.services.app.loadbalancer.server.port=8080"

Health Checks

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

Dependency Management

services:
  app:
    depends_on:
      database:
        condition: service_healthy
        
  database:
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]

GitOps Integration

Portainer Stack Deployment

  • Repository: https://git.vish.gg/Vish/homelab.git
  • Branch: main
  • Compose file path: host-name/service-name.yml
  • Environment variables: Managed in Portainer UI

File Path Standards

Atlantis/service-name.yml          # Primary NAS services
Calypso/service-name.yml           # Secondary NAS services
homelab_vm/service-name.yml        # VM-based services
concord_nuc/service-name.yml       # NUC services
raspberry-pi-5-vish/service-name.yml  # Pi services

Environment File Management

# .env file structure
PUID=1000
PGID=1000
TZ=America/New_York
SERVICE_PORT=8080
DATA_PATH=/mnt/storage/service-name

Service Categories

Media Services

services:
  plex:
    image: plexinc/pms-docker:latest
    environment:
      - PLEX_CLAIM=claim-token
      - PLEX_UID=1000
      - PLEX_GID=1000
    volumes:
      - plex-config:/config
      - /mnt/media:/media:ro
    ports:
      - "32400:32400"

Database Services

services:
  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=appdb
      - POSTGRES_USER=appuser
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - postgres-data:/var/lib/postgresql/data

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

Web Applications

services:
  webapp:
    image: nginx:alpine
    volumes:
      - ./html:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webapp.rule=Host(`app.local`)"

Monitoring Integration

Prometheus Metrics

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

Logging Configuration

services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        
    # Or use centralized logging
    logging:
      driver: "loki"
      options:
        loki-url: "http://loki:3100/loki/api/v1/push"

Backup Considerations

Volume Backup Strategy

# Backup-friendly volume structure
volumes:
  app-config:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/backup/app/config
      
  app-data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/backup/app/data

Database Backup

services:
  db-backup:
    image: postgres:15-alpine
    command: |
      sh -c "
      while true; do
        pg_dump -h postgres -U $$POSTGRES_USER $$POSTGRES_DB > /backup/backup_$$(date +%Y%m%d_%H%M%S).sql
        sleep 86400
      done"
    volumes:
      - ./backups:/backup
    depends_on:
      - postgres

Troubleshooting

Common Issues

Port Conflicts

# Check port usage
netstat -tulpn | grep :8080
docker ps --format "table {{.Names}}\t{{.Ports}}"

Volume Permissions

# Fix volume permissions
sudo chown -R 1000:1000 /path/to/volume
sudo chmod -R 755 /path/to/volume

Network Issues

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

# Test connectivity
docker exec container-name ping other-container

Debugging Commands

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

# Execute commands in container
docker-compose exec service-name bash

# Validate compose file
docker-compose config

# Check service status
docker-compose ps

Performance Optimization

Resource Management

services:
  app:
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'
    
    # Use init system for proper signal handling
    init: true
    
    # Optimize for specific workloads
    sysctls:
      - net.core.somaxconn=1024

Storage Optimization

# Use tmpfs for temporary data
tmpfs:
  - /tmp:size=100M,noexec,nosuid,nodev

# Optimize volume drivers
volumes:
  fast-data:
    driver: local
    driver_opts:
      type: tmpfs
      device: tmpfs
      o: size=1G

Validation and Testing

Pre-deployment Checks

# Validate syntax
docker-compose config

# Check for security issues
docker-compose config | docker run --rm -i hadolint/hadolint

# Test deployment
docker-compose up --dry-run

Health Monitoring

services:
  app:
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Status: Docker Compose standards implemented across all homelab services