Skip to main content

Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications.

The docker-compose.yml File

Compose uses a YAML file to configure your application’s services.

Complete Example

version: '3.8'

services:
  # 1. Frontend Service
  web:
    build: 
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - API_URL=http://api:8080
    depends_on:
      - api
    networks:
      - app-net

  # 2. Backend Service
  api:
    image: my-api:latest
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=db
      - DB_PASS=${DB_PASSWORD} # Uses .env file
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-net

  # 3. Database Service
  db:
    image: postgres:14-alpine
    environment:
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

# 4. Volumes
volumes:
  db-data:

# 5. Networks
networks:
  app-net:
    driver: bridge

Real-World Architecture Example

A typical production setup with Nginx Reverse Proxy, Backend API, Redis Cache, and PostgreSQL Database.
version: '3.8'

services:
  # Reverse Proxy (Entry Point)
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - api
      - web
    networks:
      - public
      - private

  # Frontend (React/Next.js)
  web:
    build: ./frontend
    environment:
      - API_URL=http://api:3000
    networks:
      - private

  # Backend API (Node/Python/Go)
  api:
    build: ./backend
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis
    networks:
      - private

  # Cache
  redis:
    image: redis:alpine
    networks:
      - private

  # Database
  db:
    image: postgres:14-alpine
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password
    networks:
      - private

networks:
  public:
  private:
    internal: true # Not accessible from outside

volumes:
  db_data:

secrets:
  db_password:
    file: ./secrets/db_password.txt

Key Concepts

Services

The computing resources of your application (containers).
  • build: Build an image from a Dockerfile.
  • image: Use an existing image from a registry.
  • depends_on: Control startup order.

Environment Variables

  • Inline: Defined directly in YAML.
  • .env file: Compose automatically reads variables from a .env file in the same directory.
# .env
DB_PASSWORD=secret123

Networking

By default, Compose sets up a single network for your app.
  • Services can reach each other by service name (e.g., web can ping db).

Essential Commands

# Start all services in background (detached)
docker-compose up -d

# Build images before starting
docker-compose up -d --build

# View logs of all services
docker-compose logs -f

# View logs of specific service
docker-compose logs -f api

# Stop and remove containers, networks
docker-compose down

# Stop and remove volumes too (WARNING: Data loss)
docker-compose down -v

# List running services
docker-compose ps

# Execute command in running service
docker-compose exec db psql -U postgres

Production vs Development

Overriding Configuration

You can use multiple Compose files to handle different environments.
  1. docker-compose.yml: Base config.
  2. docker-compose.prod.yml: Production overrides (restart policies, extra volumes).
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Key Takeaways

  • Use Docker Compose for local development and testing.
  • Use .env files to manage secrets and configuration.
  • Use depends_on with healthchecks to ensure services start in the correct order.
  • Use Volumes to persist database data.

Next: Docker Best Practices →