docker-compose-orchestration

Orchestrate multi-container applications with declarative YAML configuration, networking, volumes, and production-ready deployments. Define entire application stacks in YAML with services, networks, volumes, and secrets; automatic service discovery enables inter-container communication by name Supports development, staging, and production workflows through compose file overrides and environment-specific configurations with health checks and restart policies Includes 16+ real-world patterns covering full-stack apps, microservices, ELK stacks, Kafka, Keycloak, and CI/CD integrations with scaling and multi-stage builds Essential commands for lifecycle management: up , down , logs , exec , build , plus debugging, resource limits, and rolling update strategies

INSTALLATION
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill docker-compose-orchestration
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Docker Compose Orchestration

A comprehensive skill for orchestrating multi-container applications using Docker Compose. This skill enables rapid development, deployment, and management of containerized applications with service definitions, networking strategies, volume management, health checks, and production-ready configurations.

When to Use This Skill

Use this skill when:

  • Building multi-container applications (microservices, full-stack apps)
  • Setting up development environments with databases, caching, and services
  • Orchestrating frontend, backend, and database services together
  • Managing service dependencies and startup order
  • Configuring networks and inter-service communication
  • Implementing persistent storage with volumes
  • Deploying applications to development, staging, or production
  • Creating reproducible development environments
  • Managing application lifecycle (start, stop, rebuild, scale)
  • Monitoring application health and implementing health checks
  • Migrating from single containers to multi-service architectures
  • Testing distributed systems locally

Core Concepts

Docker Compose Philosophy

Docker Compose simplifies multi-container application management through:

  • Declarative Configuration: Define entire application stacks in YAML
  • Service Abstraction: Each component is a service with its own configuration
  • Automatic Networking: Services can communicate by name automatically
  • Volume Management: Persistent data and shared storage across containers
  • Environment Isolation: Each project gets its own network namespace
  • Reproducibility: Same configuration works across all environments

Key Docker Compose Entities

  • Services: Individual containers and their configurations
  • Networks: Communication channels between services
  • Volumes: Persistent storage and data sharing
  • Configs: Non-sensitive configuration files
  • Secrets: Sensitive data (passwords, API keys)
  • Projects: Collection of services under a single namespace

Compose File Structure

version: "3.8"  # Compose file format version

services:       # Define containers

  service-name:

    # Service configuration

networks:       # Define custom networks

  network-name:

    # Network configuration

volumes:        # Define named volumes

  volume-name:

    # Volume configuration

configs:        # Application configs (optional)

  config-name:

    # Config source

secrets:        # Sensitive data (optional)

  secret-name:

    # Secret source

Service Definition Patterns

Basic Service Definition

services:

  web:

    image: nginx:alpine           # Use existing image

    container_name: my-web        # Custom container name

    restart: unless-stopped       # Restart policy

    ports:

      - "80:80"                   # Host:Container port mapping

    environment:

      - ENV_VAR=value             # Environment variables

    volumes:

      - ./html:/usr/share/nginx/html  # Volume mount

    networks:

      - frontend                  # Connect to network

Build-Based Service

services:

  app:

    build:

      context: ./app              # Build context directory

      dockerfile: Dockerfile      # Custom Dockerfile

      args:                       # Build arguments

        NODE_ENV: development

      target: development         # Multi-stage build target

    image: myapp:latest           # Tag resulting image

    ports:

      - "3000:3000"

Service with Dependencies

services:

  web:

    image: nginx

    depends_on:

      db:

        condition: service_healthy  # Wait for health check

      redis:

        condition: service_started  # Wait for start only

  db:

    image: postgres:15

    healthcheck:

      test: ["CMD-SHELL", "pg_isready -U postgres"]

      interval: 10s

      timeout: 5s

      retries: 5

      start_period: 30s

  redis:

    image: redis:alpine

Service with Advanced Configuration

services:

  backend:

    build: ./backend

    command: npm run dev          # Override default command

    working_dir: /app             # Set working directory

    user: "1000:1000"             # Run as specific user

    hostname: api-server          # Custom hostname

    domainname: example.com       # Domain name

    env_file:

      - .env                      # Load env from file

      - .env.local

    environment:

      DATABASE_URL: "postgresql://db:5432/myapp"

      REDIS_URL: "redis://cache:6379"

    volumes:

      - ./backend:/app            # Source code mount

      - /app/node_modules         # Preserve node_modules

      - app-data:/data            # Named volume

    ports:

      - "3000:3000"               # Application port

      - "9229:9229"               # Debug port

    expose:

      - "8080"                    # Expose to other services only

    networks:

      - backend

      - frontend

    labels:

      - "com.example.description=Backend API"

      - "com.example.version=1.0"

    logging:

      driver: json-file

      options:

        max-size: "10m"

        max-file: "3"

    deploy:

      resources:

        limits:

          cpus: '2'

          memory: 1G

        reservations:

          cpus: '0.5'

          memory: 512M

Multi-Container Application Patterns

Pattern 1: Full-Stack Web Application

Scenario: React frontend + Node.js backend + PostgreSQL database

version: "3.8"

services:

  # Frontend React Application

  frontend:

    build:

      context: ./frontend

      dockerfile: Dockerfile

      target: development

    ports:

      - "3000:3000"

    volumes:

      - ./frontend/src:/app/src

      - /app/node_modules

    environment:

      - REACT_APP_API_URL=http://localhost:4000/api

      - CHOKIDAR_USEPOLLING=true  # For hot reload

    networks:

      - frontend

    depends_on:

      - backend

  # Backend Node.js API

  backend:

    build:

      context: ./backend

      dockerfile: Dockerfile

    ports:

      - "4000:4000"

      - "9229:9229"  # Debugger

    volumes:

      - ./backend:/app

      - /app/node_modules

    environment:

      - NODE_ENV=development

      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp

      - REDIS_URL=redis://cache:6379

      - JWT_SECRET=dev-secret

    env_file:

      - ./backend/.env.local

    networks:

      - frontend

      - backend

    depends_on:

      db:

        condition: service_healthy

      cache:

        condition: service_started

    command: npm run dev

  # PostgreSQL Database

  db:

    image: postgres:15-alpine

    container_name: postgres-db

    restart: unless-stopped

    ports:

      - "5432:5432"

    environment:

      - POSTGRES_USER=postgres

      - POSTGRES_PASSWORD=password

      - POSTGRES_DB=myapp

    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 postgres"]

      interval: 10s

      timeout: 5s

      retries: 5

  # Redis Cache

  cache:

    image: redis:7-alpine

    container_name: redis-cache

    restart: unless-stopped

    ports:

      - "6379:6379"

    volumes:

      - redis-data:/data

    networks:

      - backend

    command: redis-server --appendonly yes

    healthcheck:

      test: ["CMD", "redis-cli", "ping"]

      interval: 10s

      timeout: 3s

      retries: 5

networks:

  frontend:

    driver: bridge

  backend:

    driver: bridge

volumes:

  postgres-data:

    driver: local

  redis-data:

    driver: local

Pattern 2: Microservices Architecture

Scenario: Multiple services with reverse proxy and service discovery

version: "3.8"

services:

  # NGINX Reverse Proxy

  proxy:

    image: nginx:alpine

    container_name: reverse-proxy

    ports:

      - "80:80"

      - "443:443"

    volumes:

      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro

      - ./nginx/conf.d:/etc/nginx/conf.d:ro

      - ./ssl:/etc/nginx/ssl:ro

    networks:

      - public

    depends_on:

      - auth-service

      - user-service

      - order-service

    restart: unless-stopped

  # Authentication Service

  auth-service:

    build: ./services/auth

    container_name: auth-service

    expose:

      - "8001"

    environment:

      - SERVICE_NAME=auth

      - DATABASE_URL=postgresql://db:5432/auth_db

      - JWT_SECRET=${JWT_SECRET}

    networks:

      - public

      - internal

    depends_on:

      db:

        condition: service_healthy

    healthcheck:

      test: ["CMD", "curl", "-f", "http://localhost:8001/health"]

      interval: 30s

      timeout: 10s

      retries: 3

  # User Service

  user-service:

    build: ./services/user

    container_name: user-service

    expose:

      - "8002"

    environment:

      - SERVICE_NAME=user

      - DATABASE_URL=postgresql://db:5432/user_db

      - AUTH_SERVICE_URL=http://auth-service:8001

    networks:

      - public

      - internal

    depends_on:

      - auth-service

      - db

    healthcheck:

      test: ["CMD", "curl", "-f", "http://localhost:8002/health"]

      interval: 30s

      timeout: 10s

      retries: 3

  # Order Service

  order-service:

    build: ./services/order

    container_name: order-service

    expose:

      - "8003"

    environment:

      - SERVICE_NAME=order

      - DATABASE_URL=postgresql://db:5432/order_db

      - USER_SERVICE_URL=http://user-service:8002

      - RABBITMQ_URL=amqp://rabbitmq:5672

    networks:

      - public

      - internal

    depends_on:

      - user-service

      - db

      - rabbitmq

    healthcheck:

      test: ["CMD", "curl", "-f", "http://localhost:8003/health"]

      interval: 30s

      timeout: 10s

      retries: 3

  # Shared PostgreSQL Database

  db:

    image: postgres:15-alpine

    container_name: postgres-db

    environment:

      - POSTGRES_USER=postgres

      - POSTGRES_PASSWORD=${DB_PASSWORD}

    volumes:

      - postgres-data:/var/lib/postgresql/data

      - ./database/init-multi-db.sql:/docker-entrypoint-initdb.d/init.sql

    networks:

      - internal

    healthcheck:

      test: ["CMD-SHELL", "pg_isready -U postgres"]

      interval: 10s

      timeout: 5s

      retries: 5

  # RabbitMQ Message Broker

  rabbitmq:

    image: rabbitmq:3-management-alpine

    container_name: rabbitmq

    ports:

      - "5672:5672"   # AMQP

      - "15672:15672" # Management UI

    environment:

      - RABBITMQ_DEFAULT_USER=admin

      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD}

    volumes:

      - rabbitmq-data:/var/lib/rabbitmq

    networks:

      - internal

    healthcheck:

      test: ["CMD", "rabbitmq-diagnostics", "ping"]

      interval: 30s

      timeout: 10s

      retries: 5

networks:

  public:

    driver: bridge

  internal:

    driver: bridge

    internal: true  # No external access

volumes:

  postgres-data:

  rabbitmq-data:

Pattern 3: Development Environment with Hot Reload

Scenario: Development setup with live code reloading and debugging

version: "3.8"

services:

  # Development Frontend

  frontend-dev:

    build:

      context: ./frontend

      dockerfile: Dockerfile.dev

    ports:

      - "3000:3000"

      - "9222:9222"  # Chrome DevTools

    volumes:

      - ./frontend:/app

      - /app/node_modules

      - /app/.next  # Next.js build cache

    environment:

      - NODE_ENV=development

      - WATCHPACK_POLLING=true

      - NEXT_PUBLIC_API_URL=http://localhost:4000

    networks:

      - dev-network

    stdin_open: true

    tty: true

    command: npm run dev

  # Development Backend

  backend-dev:

    build:

      context: ./backend

      dockerfile: Dockerfile.dev

    ports:

      - "4000:4000"

      - "9229:9229"  # Node.js debugger

    volumes:

      - ./backend:/app

      - /app/node_modules

    environment:

      - NODE_ENV=development

      - DEBUG=app:*

      - DATABASE_URL=postgresql://postgres:dev@db:5432/dev_db

    networks:

      - dev-network

    depends_on:

      - db

      - mailhog

    command: npm run dev:debug

  # PostgreSQL with pgAdmin

  db:

    image: postgres:15-alpine

    environment:

      - POSTGRES_PASSWORD=dev

      - POSTGRES_DB=dev_db

    ports:

      - "5432:5432"

    volumes:

      - dev-db-data:/var/lib/postgresql/data

    networks:

      - dev-network

  pgadmin:

    image: dpage/pgadmin4:latest

    environment:

      - PGADMIN_DEFAULT_EMAIL=admin@dev.local

      - PGADMIN_DEFAULT_PASSWORD=admin

    ports:

      - "5050:80"

    networks:

      - dev-network

    depends_on:

      - db

  # MailHog for Email Testing

  mailhog:

    image: mailhog/mailhog:latest

    ports:

      - "1025:1025"  # SMTP

      - "8025:8025"  # Web UI

    networks:

      - dev-network

networks:

  dev-network:

    driver: bridge

volumes:

  dev-db-data:

Networking Strategies

Default Bridge Network

services:

  web:

    image: nginx

    # Automatically connected to default network

  app:

    image: myapp

    # Can communicate with 'web' via service name

Custom Bridge Networks

version: "3.8"

services:

  frontend:

    image: react-app

    networks:

      - public

  backend:

    image: api-server

    networks:

      - public    # Accessible from frontend

      - private   # Accessible from database

  database:

    image: postgres

    networks:

      - private   # Isolated from frontend

networks:

  public:

    driver: bridge

  private:

    driver: bridge

    internal: true  # No internet access

Network Aliases

services:

  api:

    image: api-server

    networks:

      backend:

        aliases:

          - api-server

          - api.internal

          - api-v1.internal

networks:

  backend:

    driver: bridge

Host Network Mode

services:

  app:

    image: myapp

    network_mode: "host"  # Use host network stack

    # No port mapping needed, uses host ports directly

Custom Network Configuration

networks:

  custom-network:

    driver: bridge

    driver_opts:

      com.docker.network.bridge.name: br-custom

    ipam:

      driver: default

      config:

        - subnet: 172.28.0.0/16

          gateway: 172.28.0.1

    labels:

      - "com.example.description=Custom network"

Volume Management

Named Volumes

version: "3.8"

services:

  db:

    image: postgres:15

    volumes:

      - postgres-data:/var/lib/postgresql/data  # Named volume

  backup:

    image: postgres:15

    volumes:

      - postgres-data:/backup:ro  # Read-only mount

    command: pg_dump -U postgres > /backup/dump.sql

volumes:

  postgres-data:

    driver: local

    driver_opts:

      type: none

      o: bind

      device: /path/on/host

Bind Mounts

services:

  web:

    image: nginx

    volumes:

      # Relative path bind mount

      - ./html:/usr/share/nginx/html

      # Absolute path bind mount

      - /var/log/nginx:/var/log/nginx

      # Read-only bind mount

      - ./config/nginx.conf:/etc/nginx/nginx.conf:ro

tmpfs Mounts (In-Memory)

services:

  app:

    image: myapp

    tmpfs:

      - /tmp

      - /run

    # Or with options:

    volumes:

      - type: tmpfs

        target: /app/cache

        tmpfs:

          size: 1000000000  # 1GB

Volume Sharing Between Services

services:

  app:

    image: myapp

    volumes:

      - shared-data:/data

  worker:

    image: worker

    volumes:

      - shared-data:/data

  backup:

    image: backup-tool

    volumes:

      - shared-data:/backup:ro

volumes:

  shared-data:

Advanced Volume Configuration

volumes:

  data:

    driver: local

    driver_opts:

      type: "nfs"

      o: "addr=10.40.0.199,nolock,soft,rw"

      device: ":/docker/example"

  cache:

    driver: local

    driver_opts:

      type: tmpfs

      device: tmpfs

      o: "size=100m,uid=1000"

  external-volume:

    external: true  # Volume created outside Compose

    name: my-existing-volume

Health Checks

HTTP Health Check

services:

  web:

    image: nginx

    healthcheck:

      test: ["CMD", "curl", "-f", "http://localhost/health"]

      interval: 30s

      timeout: 10s

      retries: 3

      start_period: 40s

Database Health Check

services:

  postgres:

    image: postgres:15

    healthcheck:

      test: ["CMD-SHELL", "pg_isready -U postgres"]

      interval: 10s

      timeout: 5s

      retries: 5

      start_period: 30s

  mysql:

    image: mysql:8

    healthcheck:

      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]

      interval: 10s

      timeout: 5s

      retries: 3

  mongodb:

    image: mongo:6

    healthcheck:

      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]

      interval: 10s

      timeout: 5s

      retries: 5

Application Health Check

services:

  app:

    build: ./app

    healthcheck:

      test: ["CMD", "node", "healthcheck.js"]

      interval: 30s

      timeout: 10s

      retries: 3

      start_period: 60s

  api:

    build: ./api

    healthcheck:

      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1"]

      interval: 30s

      timeout: 10s

      retries: 3

Complex Health Checks

services:

  redis:

    image: redis:alpine

    healthcheck:

      test: |

        sh -c '

        redis-cli ping | grep PONG &&

        redis-cli --raw incr ping | grep 1

        '

      interval: 10s

      timeout: 3s

      retries: 5

Development vs Production Configurations

Base Configuration (compose.yaml)

version: "3.8"

services:

  web:

    image: myapp:latest

    environment:

      - NODE_ENV=production

    networks:

      - app-network

  db:

    image: postgres:15-alpine

    networks:

      - app-network

networks:

  app-network:

    driver: bridge

Development Override (compose.override.yaml)

# Automatically merged with compose.yaml in development

version: "3.8"

services:

  web:

    build:

      context: .

      target: development

    volumes:

      - ./src:/app/src  # Live code reload

      - /app/node_modules

    ports:

      - "3000:3000"     # Expose for local access

      - "9229:9229"     # Debugger port

    environment:

      - NODE_ENV=development

      - DEBUG=*

    command: npm run dev

  db:

    ports:

      - "5432:5432"     # Expose for local tools

    environment:

      - POSTGRES_PASSWORD=dev

    volumes:

      - ./init-dev.sql:/docker-entrypoint-initdb.d/init.sql

Production Configuration (compose.prod.yaml)

version: "3.8"

services:

  web:

    image: myapp:${VERSION:-latest}

    restart: always

    environment:

      - NODE_ENV=production

    deploy:

      replicas: 3

      resources:

        limits:

          cpus: '2'

          memory: 2G

        reservations:

          cpus: '1'

          memory: 1G

      update_config:

        parallelism: 1

        delay: 10s

        failure_action: rollback

      rollback_config:

        parallelism: 1

        delay: 5s

    logging:

      driver: json-file

      options:

        max-size: "10m"

        max-file: "5"

  db:

    image: postgres:15-alpine

    restart: always

    environment:

      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password

    secrets:

      - db_password

    volumes:

      - postgres-data:/var/lib/postgresql/data

    deploy:

      resources:

        limits:

          cpus: '2'

          memory: 4G

  # Production additions

  nginx:

    image: nginx:alpine

    ports:

      - "80:80"

      - "443:443"

    volumes:

      - ./nginx/prod.conf:/etc/nginx/nginx.conf:ro

      - ssl-certs:/etc/nginx/ssl:ro

    restart: always

    depends_on:

      - web

secrets:

  db_password:

    external: true

volumes:

  postgres-data:

    driver: local

  ssl-certs:

    external: true

Staging Configuration (compose.staging.yaml)

version: "3.8"

services:

  web:

    image: myapp:staging-${VERSION:-latest}

    restart: unless-stopped

    environment:

      - NODE_ENV=staging

    deploy:

      replicas: 2

      resources:

        limits:

          cpus: '1'

          memory: 1G

  db:

    environment:

      - POSTGRES_PASSWORD=${DB_PASSWORD}

    volumes:

      - staging-db-data:/var/lib/postgresql/data

volumes:

  staging-db-data:

Essential Docker Compose Commands

Project Management

# Start services

docker compose up                    # Foreground

docker compose up -d                 # Detached (background)

docker compose up --build            # Rebuild images

docker compose up --force-recreate   # Recreate containers

docker compose up --scale web=3      # Scale service to 3 instances

# Stop services

docker compose stop                  # Stop containers

docker compose down                  # Stop and remove containers/networks

docker compose down -v               # Also remove volumes

docker compose down --rmi all        # Also remove images

# Restart services

docker compose restart               # Restart all services

docker compose restart web           # Restart specific service

Service Management

# Build services

docker compose build                 # Build all services

docker compose build web             # Build specific service

docker compose build --no-cache      # Build without cache

docker compose build --pull          # Pull latest base images

# View services

docker compose ps                    # List containers

docker compose ps -a                 # Include stopped containers

docker compose top                   # Display running processes

docker compose images                # List images

# Logs

docker compose logs                  # View all logs

docker compose logs -f               # Follow logs

docker compose logs web              # Service-specific logs

docker compose logs --tail=100 web   # Last 100 lines

Execution and Debugging

# Execute commands

docker compose exec web sh           # Interactive shell

docker compose exec web npm test     # Run command

docker compose exec -u root web sh   # Run as root

# Run one-off commands

docker compose run web npm install   # Run command in new container

docker compose run --rm web test     # Remove container after

docker compose run --no-deps web sh  # Don't start dependencies

Configuration Management

# Multiple compose files

docker compose -f compose.yaml -f compose.prod.yaml up

# Environment-specific deployment

docker compose --env-file .env.prod up

docker compose -p myproject up       # Custom project name

# Configuration validation

docker compose config                # Validate and view config

docker compose config --quiet        # Only validation

docker compose config --services     # List services

docker compose config --volumes      # List volumes

15+ Compose Examples

Example 1: NGINX + PHP + MySQL (LAMP Stack)

version: "3.8"

services:

  nginx:

    image: nginx:alpine

    ports:

      - "80:80"

    volumes:

      - ./public:/var/www/html

      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro

    networks:

      - lamp

    depends_on:

      - php

  php:

    build:

      context: ./php

      dockerfile: Dockerfile

    volumes:

      - ./public:/var/www/html

    networks:

      - lamp

    depends_on:

      - mysql

  mysql:

    image: mysql:8.0

    environment:

      MYSQL_ROOT_PASSWORD: secret

      MYSQL_DATABASE: myapp

      MYSQL_USER: user

      MYSQL_PASSWORD: password

    volumes:

      - mysql-data:/var/lib/mysql

    networks:

      - lamp

networks:

  lamp:

volumes:

  mysql-data:

Example 2: Django + PostgreSQL + Redis + Celery

version: "3.8"

services:

  web:

    build: .

    command: python manage.py runserver 0.0.0.0:8000

    volumes:

      - .:/code

    ports:

      - "8000:8000"

    environment:

      - DATABASE_URL=postgresql://postgres:postgres@db:5432/django_db

      - REDIS_URL=redis://redis:6379/0

    depends_on:

      - db

      - redis

  db:

    image: postgres:15-alpine

    environment:

      POSTGRES_DB: django_db

      POSTGRES_USER: postgres

      POSTGRES_PASSWORD: postgres

    volumes:

      - postgres-data:/var/lib/postgresql/data

  redis:

    image: redis:alpine

    volumes:

      - redis-data:/data

  celery:

    build: .

    command: celery -A myproject worker -l info

    volumes:

      - .:/code

    environment:

      - DATABASE_URL=postgresql://postgres:postgres@db:5432/django_db

      - REDIS_URL=redis://redis:6379/0

    depends_on:

      - db

      - redis

  celery-beat:

    build: .

    command: celery -A myproject beat -l info

    volumes:

      - .:/code

    environment:

      - DATABASE_URL=postgresql://postgres:postgres@db:5432/django_db

      - REDIS_URL=redis://redis:6379/0

    depends_on:

      - db

      - redis

volumes:

  postgres-data:

  redis-data:

Example 3: React + Node.js + MongoDB + NGINX

version: "3.8"

services:

  frontend:

    build:

      context: ./frontend

      args:

        REACT_APP_API_URL: http://localhost/api

    volumes:

      - ./frontend:/app

      - /app/node_modules

    environment:

      - CHOKIDAR_USEPOLLING=true

    networks:

      - app-network

  backend:

    build: ./backend

    ports:

      - "5000:5000"

    volumes:

      - ./backend:/app

      - /app/node_modules

    environment:

      - MONGODB_URI=mongodb://mongo:27017/myapp

      - JWT_SECRET=dev-secret

    depends_on:

      - mongo

    networks:

      - app-network

  mongo:

    image: mongo:6

    ports:

      - "27017:27017"

    volumes:

      - mongo-data:/data/db

      - mongo-config:/data/configdb

    environment:

      - MONGO_INITDB_ROOT_USERNAME=admin

      - MONGO_INITDB_ROOT_PASSWORD=secret

    networks:

      - app-network

  nginx:

    image: nginx:alpine

    ports:

      - "80:80"

    volumes:

      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro

    depends_on:

      - frontend

      - backend

    networks:

      - app-network

networks:

  app-network:

    driver: bridge

volumes:

  mongo-data:

  mongo-config:

Example 4: Spring Boot + MySQL + Adminer

version: "3.8"

services:

  app:

    build:

      context: .

      dockerfile: Dockerfile

    ports:

      - "8080:8080"

    environment:

      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/springdb?useSSL=false

      - SPRING_DATASOURCE_USERNAME=root

      - SPRING_DATASOURCE_PASSWORD=secret

      - SPRING_JPA_HIBERNATE_DDL_AUTO=update

    depends_on:

      db:

        condition: service_healthy

    networks:

      - spring-network

  db:

    image: mysql:8.0

    environment:

      MYSQL_ROOT_PASSWORD: secret

      MYSQL_DATABASE: springdb

    volumes:

      - mysql-data:/var/lib/mysql

    networks:

      - spring-network

    healthcheck:

      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]

      interval: 10s

      timeout: 5s

      retries: 5

  adminer:

    image: adminer:latest

    ports:

      - "8081:8080"

    environment:

      ADMINER_DEFAULT_SERVER: db

    networks:

      - spring-network

networks:

  spring-network:

volumes:

  mysql-data:

Example 5: WordPress + MySQL + phpMyAdmin

version: "3.8"

services:

  wordpress:

    image: wordpress:latest

    ports:

      - "8000:80"

    environment:

      WORDPRESS_DB_HOST: db:3306

      WORDPRESS_DB_USER: wordpress

      WORDPRESS_DB_PASSWORD: wordpress

      WORDPRESS_DB_NAME: wordpress

    volumes:

      - wordpress-data:/var/www/html

    depends_on:

      - db

    networks:

      - wordpress-network

  db:

    image: mysql:8.0

    environment:

      MYSQL_DATABASE: wordpress

      MYSQL_USER: wordpress

      MYSQL_PASSWORD: wordpress

      MYSQL_ROOT_PASSWORD: rootpassword

    volumes:

      - db-data:/var/lib/mysql

    networks:

      - wordpress-network

  phpmyadmin:

    image: phpmyadmin/phpmyadmin:latest

    ports:

      - "8080:80"

    environment:

      PMA_HOST: db

      PMA_USER: root

      PMA_PASSWORD: rootpassword

    depends_on:

      - db

    networks:

      - wordpress-network

networks:

  wordpress-network:

volumes:

  wordpress-data:

  db-data:

Example 6: Elasticsearch + Kibana + Logstash (ELK Stack)

version: "3.8"

services:

  elasticsearch:

    image: docker.elastic.co/elasticsearch/elasticsearch:8.10.0

    container_name: elasticsearch

    environment:

      - discovery.type=single-node

      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"

      - xpack.security.enabled=false

    ports:

      - "9200:9200"

      - "9300:9300"

    volumes:

      - elasticsearch-data:/usr/share/elasticsearch/data

    networks:

      - elk

  logstash:

    image: docker.elastic.co/logstash/logstash:8.10.0

    container_name: logstash

    volumes:

      - ./logstash/pipeline:/usr/share/logstash/pipeline:ro

      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro

    ports:

      - "5000:5000"

      - "9600:9600"

    environment:

      LS_JAVA_OPTS: "-Xmx256m -Xms256m"

    networks:

      - elk

    depends_on:

      - elasticsearch

  kibana:

    image: docker.elastic.co/kibana/kibana:8.10.0

    container_name: kibana

    ports:

      - "5601:5601"

    environment:

      ELASTICSEARCH_URL: http://elasticsearch:9200

      ELASTICSEARCH_HOSTS: http://elasticsearch:9200

    networks:

      - elk

    depends_on:

      - elasticsearch

networks:

  elk:

    driver: bridge

volumes:

  elasticsearch-data:

Example 7: GitLab + GitLab Runner

version: "3.8"

services:

  gitlab:

    image: gitlab/gitlab-ce:latest

    container_name: gitlab

    restart: unless-stopped

    hostname: gitlab.local

    environment:

      GITLAB_OMNIBUS_CONFIG: |

        external_url 'http://gitlab.local'

        gitlab_rails['gitlab_shell_ssh_port'] = 2222

    ports:

      - "80:80"

      - "443:443"

      - "2222:22"

    volumes:

      - gitlab-config:/etc/gitlab

      - gitlab-logs:/var/log/gitlab

      - gitlab-data:/var/opt/gitlab

    networks:

      - gitlab-network

  gitlab-runner:

    image: gitlab/gitlab-runner:latest

    container_name: gitlab-runner

    restart: unless-stopped

    volumes:

      - gitlab-runner-config:/etc/gitlab-runner

      - /var/run/docker.sock:/var/run/docker.sock

    networks:

      - gitlab-network

    depends_on:

      - gitlab

networks:

  gitlab-network:

volumes:

  gitlab-config:

  gitlab-logs:

  gitlab-data:

  gitlab-runner-config:

Example 8: Jenkins + Docker-in-Docker

version: "3.8"

services:

  jenkins:

    image: jenkins/jenkins:lts

    container_name: jenkins

    user: root

    ports:

      - "8080:8080"

      - "50000:50000"

    volumes:

      - jenkins-data:/var/jenkins_home

      - /var/run/docker.sock:/var/run/docker.sock

      - /usr/bin/docker:/usr/bin/docker

    environment:

      - JAVA_OPTS=-Djenkins.install.runSetupWizard=false

    networks:

      - jenkins-network

  jenkins-agent:

    image: jenkins/inbound-agent:latest

    container_name: jenkins-agent

    environment:

      - JENKINS_URL=http://jenkins:8080

      - JENKINS_AGENT_NAME=agent1

      - JENKINS_SECRET=${AGENT_SECRET}

      - JENKINS_AGENT_WORKDIR=/home/jenkins/agent

    volumes:

      - /var/run/docker.sock:/var/run/docker.sock

    networks:

      - jenkins-network

    depends_on:

      - jenkins

networks:

  jenkins-network:

volumes:

  jenkins-data:

Example 9: Prometheus + Grafana + Node Exporter

version: "3.8"

services:

  prometheus:

    image: prom/prometheus:latest

    container_name: prometheus

    ports:

      - "9090:9090"

    volumes:

      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro

      - prometheus-data:/prometheus

    command:

      - '--config.file=/etc/prometheus/prometheus.yml'

      - '--storage.tsdb.path=/prometheus'

    networks:

      - monitoring

  grafana:

    image: grafana/grafana:latest

    container_name: grafana

    ports:

      - "3000:3000"

    environment:

      - GF_SECURITY_ADMIN_USER=admin

      - GF_SECURITY_ADMIN_PASSWORD=admin

      - GF_INSTALL_PLUGINS=grafana-piechart-panel

    volumes:

      - grafana-data:/var/lib/grafana

      - ./grafana/provisioning:/etc/grafana/provisioning:ro

    networks:

      - monitoring

    depends_on:

      - prometheus

  node-exporter:

    image: prom/node-exporter:latest

    container_name: node-exporter

    ports:

      - "9100:9100"

    volumes:

      - /proc:/host/proc:ro

      - /sys:/host/sys:ro

      - /:/rootfs:ro

    command:

      - '--path.procfs=/host/proc'

      - '--path.sysfs=/host/sys'

      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'

    networks:

      - monitoring

networks:

  monitoring:

volumes:

  prometheus-data:

  grafana-data:

Example 10: RabbitMQ + Multiple Consumers

version: "3.8"

services:

  rabbitmq:

    image: rabbitmq:3-management-alpine

    container_name: rabbitmq

    ports:

      - "5672:5672"   # AMQP

      - "15672:15672" # Management UI

    environment:

      RABBITMQ_DEFAULT_USER: admin

      RABBITMQ_DEFAULT_PASS: secret

    volumes:

      - rabbitmq-data:/var/lib/rabbitmq

      - ./rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro

    networks:

      - messaging

    healthcheck:

      test: ["CMD", "rabbitmq-diagnostics", "ping"]

      interval: 30s

      timeout: 10s

      retries: 5

  producer:

    build: ./services/producer

    environment:

      RABBITMQ_URL: amqp://admin:secret@rabbitmq:5672

    depends_on:

      rabbitmq:

        condition: service_healthy

    networks:

      - messaging

  consumer-1:

    build: ./services/consumer

    environment:

      RABBITMQ_URL: amqp://admin:secret@rabbitmq:5672

      WORKER_ID: 1

    depends_on:

      rabbitmq:

        condition: service_healthy

    networks:

      - messaging

    deploy:

      replicas: 3

  consumer-2:

    build: ./services/consumer

    environment:

      RABBITMQ_URL: amqp://admin:secret@rabbitmq:5672

      WORKER_ID: 2

    depends_on:

      rabbitmq:

        condition: service_healthy

    networks:

      - messaging

networks:

  messaging:

volumes:

  rabbitmq-data:

Example 11: Traefik Reverse Proxy

version: "3.8"

services:

  traefik:

    image: traefik:v2.10

    container_name: traefik

    command:

      - --api.insecure=true

      - --providers.docker=true

      - --providers.docker.exposedbydefault=false

      - --entrypoints.web.address=:80

      - --entrypoints.websecure.address=:443

    ports:

      - "80:80"

      - "443:443"

      - "8080:8080"  # Traefik dashboard

    volumes:

      - /var/run/docker.sock:/var/run/docker.sock:ro

      - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro

      - ./traefik/dynamic:/etc/traefik/dynamic:ro

    networks:

      - traefik-network

  whoami:

    image: traefik/whoami

    labels:

      - "traefik.enable=true"

      - "traefik.http.routers.whoami.rule=Host(`whoami.local`)"

      - "traefik.http.routers.whoami.entrypoints=web"

    networks:

      - traefik-network

  app:

    image: nginx:alpine

    labels:

      - "traefik.enable=true"

      - "traefik.http.routers.app.rule=Host(`app.local`)"

      - "traefik.http.routers.app.entrypoints=web"

      - "traefik.http.services.app.loadbalancer.server.port=80"

    networks:

      - traefik-network

networks:

  traefik-network:

    driver: bridge

Example 12: MinIO + PostgreSQL Backup

version: "3.8"

services:

  minio:

    image: minio/minio:latest

    container_name: minio

    command: server /data --console-address ":9001"

    ports:

      - "9000:9000"

      - "9001:9001"

    environment:

      MINIO_ROOT_USER: minioadmin

      MINIO_ROOT_PASSWORD: minioadmin

    volumes:

      - minio-data:/data

    networks:

      - storage

    healthcheck:

      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]

      interval: 30s

      timeout: 20s

      retries: 3

  postgres:

    image: postgres:15-alpine

    environment:

      POSTGRES_DB: myapp

      POSTGRES_USER: postgres

      POSTGRES_PASSWORD: secret

    volumes:

      - postgres-data:/var/lib/postgresql/data

    networks:

      - storage

  backup:

    image: postgres:15-alpine

    environment:

      POSTGRES_HOST: postgres

      POSTGRES_DB: myapp

      POSTGRES_USER: postgres

      POSTGRES_PASSWORD: secret

      MINIO_ENDPOINT: minio:9000

      MINIO_ACCESS_KEY: minioadmin

      MINIO_SECRET_KEY: minioadmin

    volumes:

      - ./scripts/backup.sh:/backup.sh:ro

    entrypoint: ["/bin/sh", "/backup.sh"]

    depends_on:

      - postgres

      - minio

    networks:

      - storage

networks:

  storage:

volumes:

  minio-data:

  postgres-data:

Example 13: Apache Kafka + Zookeeper

version: "3.8"

services:

  zookeeper:

    image: confluentinc/cp-zookeeper:latest

    container_name: zookeeper

    environment:

      ZOOKEEPER_CLIENT_PORT: 2181

      ZOOKEEPER_TICK_TIME: 2000

    ports:

      - "2181:2181"

    volumes:

      - zookeeper-data:/var/lib/zookeeper/data

      - zookeeper-logs:/var/lib/zookeeper/log

    networks:

      - kafka-network

  kafka:

    image: confluentinc/cp-kafka:latest

    container_name: kafka

    depends_on:

      - zookeeper

    ports:

      - "9092:9092"

      - "29092:29092"

    environment:

      KAFKA_BROKER_ID: 1

      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092

      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT

      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT

      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

    volumes:

      - kafka-data:/var/lib/kafka/data

    networks:

      - kafka-network

  kafka-ui:

    image: provectuslabs/kafka-ui:latest

    container_name: kafka-ui

    depends_on:

      - kafka

    ports:

      - "8080:8080"

    environment:

      KAFKA_CLUSTERS_0_NAME: local

      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092

      KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper:2181

    networks:

      - kafka-network

networks:

  kafka-network:

volumes:

  zookeeper-data:

  zookeeper-logs:

  kafka-data:

Example 14: Keycloak + PostgreSQL (Identity & Access Management)

version: "3.8"

services:

  postgres:

    image: postgres:15-alpine

    container_name: keycloak-db

    environment:

      POSTGRES_DB: keycloak

      POSTGRES_USER: keycloak

      POSTGRES_PASSWORD: password

    volumes:

      - postgres-data:/var/lib/postgresql/data

    networks:

      - keycloak-network

  keycloak:

    image: quay.io/keycloak/keycloak:latest

    container_name: keycloak

    environment:

      KC_DB: postgres

      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak

      KC_DB_USERNAME: keycloak

      KC_DB_PASSWORD: password

      KEYCLOAK_ADMIN: admin

      KEYCLOAK_ADMIN_PASSWORD: admin

    command: start-dev

    ports:

      - "8080:8080"

    depends_on:

      - postgres

    networks:

      - keycloak-network

networks:

  keycloak-network:

volumes:

  postgres-data:

Example 15: Portainer (Docker Management UI)

version: "3.8"

services:

  portainer:

    image: portainer/portainer-ce:latest

    container_name: portainer

    restart: unless-stopped

    ports:

      - "9000:9000"

      - "8000:8000"

    volumes:

      - /var/run/docker.sock:/var/run/docker.sock

      - portainer-data:/data

    networks:

      - portainer-network

networks:

  portainer-network:

volumes:

  portainer-data:

Example 16: SonarQube + PostgreSQL (Code Quality)

version: "3.8"

services:

  sonarqube:

    image: sonarqube:community

    container_name: sonarqube

    depends_on:

      - db

    environment:

      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar

      SONAR_JDBC_USERNAME: sonar

      SONAR_JDBC_PASSWORD: sonar

    volumes:

      - sonarqube-conf:/opt/sonarqube/conf

      - sonarqube-data:/opt/sonarqube/data

      - sonarqube-logs:/opt/sonarqube/logs

      - sonarqube-extensions:/opt/sonarqube/extensions

    ports:

      - "9000:9000"

    networks:

      - sonarqube-network

  db:

    image: postgres:15-alpine

    container_name: sonarqube-db

    environment:

      POSTGRES_USER: sonar

      POSTGRES_PASSWORD: sonar

      POSTGRES_DB: sonar

    volumes:

      - postgresql-data:/var/lib/postgresql/data

    networks:

      - sonarqube-network

networks:

  sonarqube-network:

volumes:

  sonarqube-conf:

  sonarqube-data:

  sonarqube-logs:

  sonarqube-extensions:

  postgresql-data:

Best Practices

Service Configuration

  • Use Specific Image Tags: Avoid latest in production
  • Health Checks: Always define health checks for critical services
  • Resource Limits: Set CPU and memory limits in production
  • Restart Policies: Use appropriate restart policies
  • Environment Variables: Use .env files for sensitive data
  • Named Volumes: Use named volumes for data persistence
  • Network Isolation: Separate frontend/backend networks
  • Logging Configuration: Set up proper log rotation

Development Workflow

  • Hot Reload: Mount source code as volumes for live updates
  • Debug Ports: Expose debugger ports in development
  • Override Files: Use compose.override.yaml for local config
  • Build Caching: Structure Dockerfiles for efficient caching
  • Separate Concerns: One process per container
  • Service Naming: Use descriptive, consistent service names

Security

  • Secrets Management: Use Docker secrets or external secret managers
  • Non-Root Users: Run containers as non-root users
  • Read-Only Filesystems: Mount volumes as read-only when possible
  • Network Segmentation: Use multiple networks for isolation
  • Environment Isolation: Never commit sensitive .env files
  • Image Scanning: Scan images for vulnerabilities
  • Minimal Base Images: Use Alpine or distroless images

Production Deployment

  • Image Versioning: Tag images with semantic versions
  • Rolling Updates: Configure gradual rollout strategies
  • Monitoring: Integrate with monitoring solutions
  • Backup Strategy: Implement automated backups
  • High Availability: Deploy replicas of critical services
  • Load Balancing: Use reverse proxies for load distribution
  • Configuration Management: Externalize configuration
  • Disaster Recovery: Test backup and restore procedures

Troubleshooting

Common Issues

Services can't communicate

  • Check network configuration
  • Verify service names are correct
  • Ensure services are on same network
  • Check firewall rules

Volumes not persisting

  • Verify named volumes are defined
  • Check volume mount paths
  • Ensure proper permissions
  • Review Docker volume driver

Services failing health checks

  • Increase start_period
  • Verify health check command
  • Check service logs
  • Ensure dependencies are ready

Port conflicts

  • Check for existing services on ports
  • Use different host ports
  • Review port mapping syntax

Build failures

  • Clear build cache: docker compose build --no-cache
  • Check Dockerfile syntax
  • Verify build context
  • Review build arguments

Debugging Commands

# View detailed container information

docker compose ps -a

docker compose logs -f service-name

docker inspect container-name

# Execute commands in running containers

docker compose exec service-name sh

docker compose exec service-name env

# Check network connectivity

docker compose exec service-name ping other-service

docker compose exec service-name netstat -tulpn

# Review configuration

docker compose config

docker compose config --services

docker compose config --volumes

# Clean up resources

docker compose down -v

docker system prune -a --volumes

Advanced Usage

Multi-Stage Builds for Optimization

services:

  app:

    build:

      context: .

      dockerfile: Dockerfile

      target: production

    # Dockerfile uses multi-stage builds
# Development stage

FROM node:18-alpine AS development

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

CMD ["npm", "run", "dev"]

# Build stage

FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci --only=production

COPY . .

RUN npm run build

# Production stage

FROM node:18-alpine AS production

WORKDIR /app

COPY --from=builder /app/dist ./dist

COPY --from=builder /app/node_modules ./node_modules

COPY package*.json ./

EXPOSE 3000

CMD ["node", "dist/index.js"]

Environment-Specific Deployments

# Development

docker compose up

# Staging

docker compose -f compose.yaml -f compose.staging.yaml up

# Production

docker compose -f compose.yaml -f compose.prod.yaml up -d

# With environment file

docker compose --env-file .env.prod -f compose.yaml -f compose.prod.yaml up -d

Scaling Services

# Scale specific service

docker compose up -d --scale worker=5

# Scale multiple services

docker compose up -d --scale worker=5 --scale consumer=3

Conditional Service Activation with Profiles

services:

  web:

    image: nginx

    # Always starts

  debug:

    image: debug-tools

    profiles:

      - debug  # Only starts with --profile debug

  test:

    build: .

    profiles:

      - test   # Only starts with --profile test
# Start with debug profile

docker compose --profile debug up

# Start with multiple profiles

docker compose --profile debug --profile test up

Quick Reference

Essential Commands

# Start and manage

docker compose up -d                    # Start detached

docker compose down                     # Stop and remove

docker compose restart                  # Restart all

docker compose stop                     # Stop without removing

# Build and pull

docker compose build                    # Build all images

docker compose pull                     # Pull all images

docker compose build --no-cache        # Clean build

# View and monitor

docker compose ps                       # List containers

docker compose logs -f                  # Follow logs

docker compose top                      # Running processes

docker compose events                   # Real-time events

# Execute and debug

docker compose exec service sh          # Interactive shell

docker compose run --rm service cmd     # One-off command

File Structure

project/

├── compose.yaml              # Base configuration

├── compose.override.yaml     # Local overrides (auto-loaded)

├── compose.prod.yaml         # Production config

├── compose.staging.yaml      # Staging config

├── .env                      # Default environment

├── .env.prod                 # Production environment

├── services/

│   ├── frontend/

│   │   ├── Dockerfile

│   │   └── src/

│   ├── backend/

│   │   ├── Dockerfile

│   │   └── src/

│   └── worker/

│       ├── Dockerfile

│       └── src/

└── docker/

    ├── nginx/

    │   └── nginx.conf

    └── scripts/

        └── init.sql

Resources

Skill Version: 1.0.0

Last Updated: October 2025

Skill Category: DevOps, Container Orchestration, Application Deployment

Compatible With: Docker Compose v3.8+, Docker Engine 20.10+

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card