Table of Contents

Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services, networks, and volumes. Then, with a single command, you create and start all the services from your configuration.

Note

Docker Compose is ideal for development, testing, and staging environments, as well as CI/CD workflows and single-host deployments.

Table of Contents

Overview

Key Features

  • Multi-Container Orchestration: Define and manage multiple containers as a single application
  • Service Dependencies: Control startup order and dependencies between services
  • Environment Management: Easy switching between development, testing, and production configurations
  • Volume Management: Persistent data storage and sharing between containers
  • Network Isolation: Create custom networks for service communication
  • Scaling: Scale services up or down as needed
  • Health Checks: Monitor container health and restart failed services

Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                          Docker Compose Architecture                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐          │
│  │   Compose File  │───►│   Docker API    │───►│   Containers    │          │
│  │                 │    │                 │    │                 │          │
│  │ • Services      │    │ • Create        │    │ • App Server    │          │
│  │ • Networks      │    │ • Start         │    │ • Database      │          │
│  │ • Volumes       │    │ • Stop          │    │ • Cache         │          │
│  │ • Environment   │    │ • Remove        │    │ • Load Balancer │          │
│  └─────────────────┘    └─────────────────┘    └─────────────────┘          │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                        Docker Network                               │    │
│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐           │    │
│  │  │   Web   │◄──►│   API   │◄──►│Database │    │  Cache  │           │    │
│  │  └─────────┘    └─────────┘    └─────────┘    └─────────┘           │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Use Cases

  • Development Environments: Consistent development setup across teams
  • Testing: Integration testing with all dependencies
  • Microservices: Orchestrate multiple services together
  • CI/CD Pipelines: Automated testing and deployment workflows
  • Single-Host Production: Simple production deployments
  • Local Development: Replace complex local setups

Installation

Docker Desktop (Windows/Mac)

Docker Compose is included with Docker Desktop:

# Verify installation
docker compose version

Linux Installation

# Download the latest stable release
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# Make it executable
sudo chmod +x /usr/local/bin/docker-compose

# Create symbolic link (optional)
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

# Verify installation
docker-compose --version

Using pip (Alternative)

# Install using pip
pip install docker-compose

# Verify installation
docker-compose --version

Using package managers

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install docker-compose

# CentOS/RHEL/Fedora
sudo dnf install docker-compose

# Arch Linux
sudo pacman -S docker-compose

# macOS (Homebrew)
brew install docker-compose

Core Concepts

Services

Services define the containers that make up your application. Each service runs one image and can be scaled to run multiple containers.

Networks

Networks enable communication between containers. By default, Compose creates a single network for your app.

Volumes

Volumes provide persistent data storage that survives container restarts and updates.

Profiles

Profiles allow you to adjust your Compose application for different environments or use cases.

Compose File Structure

Basic Structure

# docker-compose.yml
version: '3.8'  # Compose file format version

services:
  # Define your services here
  web:
    # Service configuration
    
networks:
  # Define custom networks (optional)
  
volumes:
  # Define named volumes (optional)

secrets:
  # Define secrets (optional)

configs:
  # Define configs (optional)

Version Compatibility

Compose Version Docker Engine Features
3.8 19.03.0+ Latest features, init, device_cgroup_rules
3.7 18.06.0+ External networks, secrets
3.6 18.02.0+ tmpfs, init
3.5 17.12.0+ Isolation, scale
3.4 17.09.0+ pid, platform
Tip

Use the latest version (3.8+) for new projects to access all features.

Services Configuration

Basic Service Definition

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    environment:
      - NGINX_HOST=localhost
      - NGINX_PORT=80
    restart: unless-stopped

Build Configuration

services:
  app:
    build: .  # Build from current directory
    # OR with advanced build options
    build:
      context: ./app
      dockerfile: Dockerfile.dev
      args:
        - NODE_ENV=development
        - API_URL=http://localhost:3000
      target: development
      cache_from:
        - myapp:latest
    image: myapp:dev

Advanced Service Configuration

services:
  api:
    image: node:16-alpine
    container_name: my-api
    hostname: api-server
    
    # Resource limits
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    
    # Health check
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    
    # Dependencies
    depends_on:
      - database
      - redis
    
    # Environment variables
    environment:
      NODE_ENV: production
      PORT: 3000
      DATABASE_URL: postgres://user:pass@database:5432/myapp
    
    # Environment file
    env_file:
      - .env
      - .env.production
    
    # Ports
    ports:
      - "3000:3000"
      - "3001:3001"
    
    # Volumes
    volumes:
      - ./app:/usr/src/app
      - node_modules:/usr/src/app/node_modules
      - logs:/var/log
    
    # Networks
    networks:
      - frontend
      - backend
    
    # Labels
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.localhost`)"
    
    # Logging
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    
    # Security
    security_opt:
      - no-new-privileges:true
    user: "1001:1001"
    read_only: true
    
    # Restart policy
    restart: unless-stopped
    
    # Command override
    command: npm start
    
    # Working directory
    working_dir: /usr/src/app
    
    # TTY and interactive
    tty: true
    stdin_open: true

Service Dependencies

services:
  web:
    image: nginx
    depends_on:
      - api
      - database
  
  api:
    image: node:16
    depends_on:
      database:
        condition: service_healthy
      redis:
        condition: service_started
  
  database:
    image: postgres:13
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
  
  redis:
    image: redis:alpine

Network Configuration

Default Network

# Automatic network creation
services:
  web:
    image: nginx
  api:
    image: node:16
# Both services can communicate using service names

Custom Networks

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

services:
  web:
    image: nginx
    networks:
      - frontend
  
  api:
    image: node:16
    networks:
      - frontend
      - backend
  
  database:
    image: postgres:13
    networks:
      - backend

Advanced Network Configuration

networks:
  frontend:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br-frontend
    ipam:
      driver: default
      config:
        - subnet: 172.28.0.0/16
          ip_range: 172.28.240.0/20
          gateway: 172.28.0.1
    labels:
      - "environment=production"
  
  backend:
    driver: bridge
    internal: true  # No external access
  
  external-network:
    external: true
    name: existing-network

services:
  proxy:
    image: nginx
    networks:
      frontend:
        ipv4_address: 172.28.0.10
      external-network:

Network Aliases

services:
  api:
    image: node:16
    networks:
      backend:
        aliases:
          - api-server
          - backend-api
  
  worker:
    image: node:16
    networks:
      - backend
    # Can connect to api using 'api', 'api-server', or 'backend-api'

Volume Management

Named Volumes

volumes:
  postgres_data:
    driver: local
  redis_data:
    driver: local
    driver_opts:
      type: tmpfs
      device: tmpfs
  
  nfs_data:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.1.100,rw
      device: ":/path/to/dir"

services:
  database:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data
  
  cache:
    image: redis:alpine
    volumes:
      - redis_data:/data

Bind Mounts

services:
  web:
    image: nginx
    volumes:
      # Bind mount
      - ./html:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      
      # Short syntax
      - ./logs:/var/log/nginx
      
      # Long syntax
      - type: bind
        source: ./config
        target: /etc/nginx/conf.d
        read_only: true
      
      # Anonymous volume
      - /var/lib/mysql

Volume Configuration Options

volumes:
  data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /host/path
    labels:
      - "project=myapp"
      - "environment=production"
  
  external_volume:
    external: true
    name: shared-data

services:
  app:
    image: myapp
    volumes:
      - type: volume
        source: data
        target: /data
        volume:
          nocopy: true
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 100M

Environment Variables

Environment Variable Sources

services:
  app:
    image: node:16
    
    # Direct environment variables
    environment:
      NODE_ENV: production
      PORT: 3000
      DEBUG: "app:*"
    
    # Array format
    environment:
      - NODE_ENV=production
      - PORT=3000
    
    # Environment files
    env_file:
      - .env
      - .env.production
      - ./config/app.env
    
    # Interpolate host environment
    environment:
      HOST_USER: ${USER}
      HOST_UID: ${UID:-1000}
      DATABASE_URL: postgres://${DB_USER}:${DB_PASS}@database:5432/${DB_NAME}

Environment File Examples

# .env
NODE_ENV=development
PORT=3000
DATABASE_URL=postgres://user:password@localhost:5432/myapp
REDIS_URL=redis://localhost:6379
API_KEY=your-secret-api-key
DEBUG=app:*

# .env.production
NODE_ENV=production
PORT=8080
DATABASE_URL=postgres://produser:prodpass@prod-db:5432/prodapp
REDIS_URL=redis://prod-redis:6379
# API_KEY should be set via secrets in production

Variable Substitution

# docker-compose.yml
services:
  app:
    image: ${APP_IMAGE:-node:16}
    ports:
      - "${APP_PORT:-3000}:3000"
    environment:
      NODE_ENV: ${NODE_ENV:-development}
      DATABASE_URL: postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}
    volumes:
      - ${HOST_DATA_PATH}:/data
# Set variables in shell or .env file
export APP_IMAGE=myapp:latest
export APP_PORT=8080
export DB_USER=postgres
export DB_PASS=secretpassword
export DB_HOST=database
export DB_PORT=5432
export DB_NAME=myapp
export HOST_DATA_PATH=/opt/myapp/data

Multi-Environment Setup

Environment-Specific Files

# docker-compose.yml (base configuration)
version: '3.8'

services:
  app:
    image: myapp:latest
    environment:
      NODE_ENV: production
    volumes:
      - ./app:/usr/src/app
    networks:
      - app-network

networks:
  app-network:
# docker-compose.override.yml (development overrides)
version: '3.8'

services:
  app:
    build: .
    environment:
      NODE_ENV: development
      DEBUG: "app:*"
    volumes:
      - ./app:/usr/src/app
      - node_modules:/usr/src/app/node_modules
    ports:
      - "3000:3000"
    command: npm run dev
  
  database:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp_dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: devpass
    ports:
      - "5432:5432"
    volumes:
      - postgres_dev:/var/lib/postgresql/data

volumes:
  postgres_dev:
  node_modules:
# docker-compose.prod.yml (production overrides)
version: '3.8'

services:
  app:
    restart: unless-stopped
    environment:
      NODE_ENV: production
    deploy:
      resources:
        limits:
          memory: 1G
    labels:
      - "traefik.enable=true"
  
  database:
    image: postgres:13
    restart: unless-stopped
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - postgres_prod:/var/lib/postgresql/data
    secrets:
      - db_password

secrets:
  db_password:
    external: true

volumes:
  postgres_prod:
    external: true

Running Different Environments

# Development (uses docker-compose.yml + docker-compose.override.yml)
docker compose up

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Testing
docker compose -f docker-compose.yml -f docker-compose.test.yml up --abort-on-container-exit

# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d

Profiles for Selective Services

services:
  app:
    image: myapp
    profiles: ["core"]
  
  database:
    image: postgres:13
    profiles: ["core", "db"]
  
  admin:
    image: myapp-admin
    profiles: ["admin"]
  
  monitoring:
    image: prometheus
    profiles: ["monitoring"]
  
  debug:
    image: myapp-debug
    profiles: ["debug"]
# Run core services only
docker compose --profile core up

# Run with admin interface
docker compose --profile core --profile admin up

# Run everything except monitoring
docker compose --profile core --profile admin --profile debug up

# Run all profiles
docker compose --profile core --profile admin --profile monitoring --profile debug up

Production Considerations

Production-Ready Configuration

version: '3.8'

services:
  app:
    image: myapp:${APP_VERSION}
    restart: unless-stopped
    
    # Resource limits
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    
    # Health checks
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    
    # Security
    security_opt:
      - no-new-privileges:true
    user: "app:app"
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=1G
    
    # Logging
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
        labels: "service,version"
    
    # Environment
    environment:
      NODE_ENV: production
    env_file:
      - .env.production
    
    # Secrets
    secrets:
      - db_password
      - api_key
    
    # Networks
    networks:
      - frontend
      - backend
    
    # Labels for service discovery
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`app.example.com`)"
      - "traefik.http.routers.app.tls=true"
      - "traefik.http.routers.app.tls.certresolver=letsencrypt"

  database:
    image: postgres:13-alpine
    restart: unless-stopped
    
    # Security
    user: postgres
    
    # Health check
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    
    # Environment
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    
    # Volumes
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    
    # Secrets
    secrets:
      - db_password
    
    # Network
    networks:
      - backend
    
    # Logging
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    
    volumes:
      - redis_data:/data
    
    networks:
      - backend
    
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    
    ports:
      - "80:80"
      - "443:443"
    
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/ssl:ro
      - nginx_logs:/var/log/nginx
    
    networks:
      - frontend
    
    depends_on:
      - app
    
    labels:
      - "traefik.enable=true"

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

volumes:
  postgres_data:
    driver: local
  redis_data:
    driver: local
  nginx_logs:
    driver: local

secrets:
  db_password:
    external: true
  api_key:
    external: true

Scaling Services

# Scale specific service
docker compose up -d --scale app=3 --scale worker=5

# Using deploy configuration
services:
  app:
    image: myapp
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3

Rolling Updates

# Update single service
docker compose up -d --no-deps app

# Rolling update with health checks
docker compose up -d --wait --wait-timeout 60

# Blue-green deployment pattern
docker compose -f docker-compose.yml -f docker-compose.blue.yml up -d
# Verify blue environment
docker compose -f docker-compose.yml -f docker-compose.green.yml up -d
# Switch traffic to green

Monitoring and Logging

Centralized Logging

services:
  app:
    image: myapp
    logging:
      driver: "fluentd"
      options:
        fluentd-address: "fluentd:24224"
        tag: "docker.myapp"
  
  fluentd:
    image: fluent/fluentd:v1.14-1
    volumes:
      - ./fluentd/fluent.conf:/fluentd/etc/fluent.conf
    ports:
      - "24224:24224"
    networks:
      - logging

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data
    networks:
      - logging

  kibana:
    image: docker.elastic.co/kibana/kibana:7.17.0
    ports:
      - "5601:5601"
    environment:
      ELASTICSEARCH_HOSTS: http://elasticsearch:9200
    networks:
      - logging

networks:
  logging:

volumes:
  elasticsearch_data:

Monitoring Stack

# monitoring-stack.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/dashboards:/var/lib/grafana/dashboards
      - ./grafana/provisioning:/etc/grafana/provisioning
    networks:
      - monitoring

  node-exporter:
    image: prom/node-exporter:latest
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    networks:
      - monitoring

volumes:
  prometheus_data:
  grafana_data:

networks:
  monitoring:

Health Monitoring

services:
  app:
    image: myapp
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    
  database:
    image: postgres:13
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    
  redis:
    image: redis:alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3

  # Health check aggregator
  healthcheck:
    image: alpine
    command: |
      sh -c '
        while true; do
          docker compose ps --services --filter "status=running" | wc -l
          sleep 30
        done
      '
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

Security Best Practices

Security Configuration

services:
  app:
    image: myapp:latest
    
    # Run as non-root user
    user: "1000:1000"
    
    # Security options
    security_opt:
      - no-new-privileges:true
      - seccomp:unconfined
    
    # Read-only root filesystem
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=100m
      - /var/cache:noexec,nosuid,size=50m
    
    # Capabilities
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    
    # Resource limits
    ulimits:
      nofile:
        soft: 1024
        hard: 2048
      nproc: 512
    
    # Environment
    environment:
      # Don't expose sensitive info in environment
      - LOG_LEVEL=info
    
    # Use secrets for sensitive data
    secrets:
      - db_password
      - api_key
    
    # Labels
    labels:
      - "security.scan=enabled"
      - "compliance.level=high"

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    external: true
    name: production_api_key

Network Security

networks:
  # Public-facing network
  public:
    driver: bridge
    
  # Internal application network
  internal:
    driver: bridge
    internal: true
    
  # Database network (most restricted)
  database:
    driver: bridge
    internal: true
    ipam:
      config:
        - subnet: 172.20.0.0/24

services:
  nginx:
    image: nginx
    networks:
      - public
      - internal
    ports:
      - "80:80"
      - "443:443"
  
  app:
    image: myapp
    networks:
      - internal
      - database
    # No external ports exposed
  
  database:
    image: postgres:13
    networks:
      - database
    # Only accessible from database network

Secrets Management

# Using Docker secrets
services:
  app:
    image: myapp
    secrets:
      - source: db_password
        target: /run/secrets/db_password
        mode: 0400
      - source: api_key
        target: /run/secrets/api_key
        mode: 0400

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    external: true
    name: myapp_api_key
# Create external secret
echo "secret-api-key" | docker secret create myapp_api_key -

# Use with Docker Compose
docker stack deploy -c docker-compose.yml myapp

Templates

Docker Compose templates for common application architectures and development scenarios:

Full-Stack Application

version: '3.8'

services:
  # Frontend
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    environment:
      - REACT_APP_API_URL=http://localhost:8080
    volumes:
      - ./frontend:/app
      - node_modules_frontend:/app/node_modules
    networks:
      - frontend

  # Backend API
  backend:
    build: ./backend
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgres://user:pass@database:5432/myapp
      - REDIS_URL=redis://redis:6379
    volumes:
      - ./backend:/app
      - node_modules_backend:/app/node_modules
    networks:
      - frontend
      - backend
    depends_on:
      database:
        condition: service_healthy
      redis:
        condition: service_started

  # Database
  database:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Cache
  redis:
    image: redis:alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - backend

  # Background worker
  worker:
    build: ./backend
    command: npm run worker
    environment:
      - DATABASE_URL=postgres://user:pass@database:5432/myapp
      - REDIS_URL=redis://redis:6379
    volumes:
      - ./backend:/app
      - node_modules_backend:/app/node_modules
    networks:
      - backend
    depends_on:
      - database
      - redis

networks:
  frontend:
  backend:
    internal: true

volumes:
  postgres_data:
  redis_data:
  node_modules_frontend:
  node_modules_backend:

Microservices Architecture

version: '3.8'

services:
  # API Gateway
  gateway:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    networks:
      - public
      - services
    depends_on:
      - user-service
      - order-service
      - product-service

  # User Service
  user-service:
    build: ./services/user
    environment:
      - SERVICE_NAME=user-service
      - DATABASE_URL=postgres://user:pass@user-db:5432/users
    networks:
      - services
      - user-db-network
    depends_on:
      - user-db

  user-db:
    image: postgres:13
    environment:
      POSTGRES_DB: users
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - user_db_data:/var/lib/postgresql/data
    networks:
      - user-db-network

  # Order Service
  order-service:
    build: ./services/order
    environment:
      - SERVICE_NAME=order-service
      - DATABASE_URL=postgres://user:pass@order-db:5432/orders
      - USER_SERVICE_URL=http://user-service:3000
    networks:
      - services
      - order-db-network
    depends_on:
      - order-db

  order-db:
    image: postgres:13
    environment:
      POSTGRES_DB: orders
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - order_db_data:/var/lib/postgresql/data
    networks:
      - order-db-network

  # Product Service
  product-service:
    build: ./services/product
    environment:
      - SERVICE_NAME=product-service
      - DATABASE_URL=mongo://product-db:27017/products
    networks:
      - services
      - product-db-network
    depends_on:
      - product-db

  product-db:
    image: mongo:5
    volumes:
      - product_db_data:/data/db
    networks:
      - product-db-network

  # Message Queue
  rabbitmq:
    image: rabbitmq:3-management
    environment:
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin
    ports:
      - "15672:15672"
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    networks:
      - services

networks:
  public:
  services:
    internal: true
  user-db-network:
    internal: true
  order-db-network:
    internal: true
  product-db-network:
    internal: true

volumes:
  user_db_data:
  order_db_data:
  product_db_data:
  rabbitmq_data:

Development Environment

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

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
      - "9229:9229"  # Node.js debugging port
    environment:
      - NODE_ENV=development
      - DEBUG=app:*
    volumes:
      - .:/app
      - node_modules:/app/node_modules
    command: npm run dev
    stdin_open: true
    tty: true

  database:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp_dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: devpass
    ports:
      - "5432:5432"
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data
      - ./database/seeds:/docker-entrypoint-initdb.d

  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

  mailhog:
    image: mailhog/mailhog
    ports:
      - "1025:1025"
      - "8025:8025"

volumes:
  postgres_dev_data:
  node_modules:

Troubleshooting

Common Issues and Solutions

Service Won't Start

# Check service logs
docker compose logs service-name

# Check all logs
docker compose logs

# Follow logs in real-time
docker compose logs -f service-name

# Check service status
docker compose ps

# Inspect service configuration
docker compose config

Port Already in Use

# Find process using port
lsof -i :3000
netstat -tulpn | grep :3000

# Kill process
kill -9 PID

# Use different port in compose file
ports:
  - "3001:3000"

Volume Permission Issues

# Check volume permissions
docker compose exec service-name ls -la /path/to/volume

# Fix permissions
docker compose exec service-name chown -R user:group /path/to/volume
# Set user in compose file
services:
  app:
    image: myapp
    user: "${UID}:${GID}"
    volumes:
      - ./data:/app/data

Network Connectivity Issues

# Test network connectivity
docker compose exec service1 ping service2
docker compose exec service1 nslookup service2
docker compose exec service1 nc -zv service2 port

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

# Check service endpoints
docker compose exec service1 cat /etc/hosts

Memory/Resource Issues

# Check resource usage
docker stats

# Monitor specific services
docker compose top

# Set resource limits
services:
  app:
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'

Debugging Techniques

Debug Mode

# Enable debug mode
export COMPOSE_DEBUG=1
docker compose up

# Verbose output
docker compose --verbose up

# Dry run (validate without executing)
docker compose --dry-run up

Service Inspection

# Inspect service
docker compose exec service-name bash

# Check environment variables
docker compose exec service-name env

# Check running processes
docker compose exec service-name ps aux

# Check network configuration
docker compose exec service-name ip addr
docker compose exec service-name netstat -tulpn

Configuration Validation

# Validate compose file syntax
docker compose config

# Show resolved configuration
docker compose config --resolve-env-vars

# Validate specific service
docker compose config service-name

# Show services
docker compose config --services

# Show volumes
docker compose config --volumes

Best Practices

File Organization

project/
├── docker-compose.yml              # Base configuration
├── docker-compose.override.yml     # Development overrides
├── docker-compose.prod.yml         # Production overrides  
├── docker-compose.test.yml         # Testing configuration
├── .env                            # Environment variables
├── .env.example                    # Environment template
├── services/
│   ├── app/
│   │   ├── Dockerfile
│   │   ├── Dockerfile.dev
│   │   └── src/
│   ├── database/
│   │   ├── init.sql
│   │   └── migrations/
│   └── nginx/
│       ├── nginx.conf
│       └── ssl/
├── config/
│   ├── prometheus/
│   ├── grafana/
│   └── fluentd/
├── scripts/
│   ├── deploy.sh
│   ├── backup.sh
│   └── monitor.sh
└── docs/
    ├── README.md
    └── DEPLOYMENT.md

Naming Conventions

# Use descriptive service names
services:
  web-frontend:          # Not: web
  api-backend:           # Not: app
  postgres-database:     # Not: db
  redis-cache:          # Not: cache
  nginx-proxy:          # Not: proxy

# Use consistent volume naming
volumes:
  postgres_data:         # service_purpose
  redis_cache_data:
  nginx_logs:

# Use meaningful network names
networks:
  frontend_network:
  backend_network:
  database_network:

Configuration Management

# Use environment variables for configuration
services:
  app:
    environment:
      - NODE_ENV=${NODE_ENV:-development}
      - PORT=${APP_PORT:-3000}
      - DATABASE_URL=${DATABASE_URL}
    
    # Use labels for metadata
    labels:
      - "com.example.service=api"
      - "com.example.version=${VERSION}"
      - "com.example.environment=${NODE_ENV}"

Performance Optimization

services:
  app:
    # Set appropriate restart policy
    restart: unless-stopped
    
    # Configure resource limits
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'
        reservations:
          memory: 512M
          cpus: '0.5'
    
    # Use health checks
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    
    # Configure logging
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Security Guidelines

  1. Use specific image tags

    image: node:16.14.2-alpine  # Not: node:latest
    
  2. Run as non-root user

    user: "1000:1000"
    
  3. Use secrets for sensitive data

    secrets:
      - db_password
      - api_key
    
  4. Limit network exposure

    # Only expose necessary ports
    ports:
      - "80:80"
    # Use internal networks for service communication
    
  5. Set security options

    security_opt:
      - no-new-privileges:true
    read_only: true
    

Development Workflow

# Start development environment
docker compose up -d

# View logs
docker compose logs -f app

# Run tests
docker compose exec app npm test

# Access database
docker compose exec database psql -U user -d myapp

# Restart service after code changes
docker compose restart app

# Stop everything
docker compose down

# Clean up (remove volumes)
docker compose down -v

Production Deployment

# Deploy to production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Update service
docker compose pull app
docker compose up -d --no-deps app

# Monitor health
docker compose ps
docker compose logs --tail=100 app

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

# Backup volumes
docker compose exec database pg_dump -U user myapp > backup.sql

Resources

Official Documentation

Learning Resources

Tools and Extensions

Community Resources