Skip to content

Docker Compose: The Complete Guide

Docker Compose lets you define and run multi-container Docker applications with a single command.

What Is Docker Compose?

Instead of running multiple docker run commands:

docker run -d --name db postgres:16
docker run -d --name api --link db my-api
docker run -d --name frontend --link api nginx

You define everything in a YAML file:

# docker-compose.yml
services:
  db:
    image: postgres:16
  api:
    build: ./api
    depends_on:
      - db
  frontend:
    build: ./frontend
    ports:
      - "80:80"

And start everything with one command: docker compose up.

Basic Structure

version: "3.9"  # optional in newer Docker Compose

services:
  # Define your containers here

networks:
  # Define custom networks

volumes:
  # Define persistent volumes

Defining Services

services:
  web:
    build: .                    # build from Dockerfile
    image: my-app:latest        # or use existing image
    ports:
      - "8000:8000"             # host:container
    environment:
      - DEBUG=true
      - DB_HOST=db
    env_file:
      - .env
    volumes:
      - ./src:/app/src          # bind mount
      - app_data:/data          # named volume
    depends_on:
      - db
      - redis
    restart: unless-stopped

Docker Compose in Action

Development

services:
  api:
    build:
      context: ./api
      target: development
    volumes:
      - ./api:/app              # live reload
    environment:
      - DEBUG=1
    ports:
      - "8000:8000"
    command: uvicorn app:app --reload --host 0.0.0.0

Production

services:
  api:
    build:
      context: ./api
      target: production
    ports:
      - "8000:8000"
    environment:
      - DEBUG=0
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

Complete Example

services:
  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}

  redis:
    image: redis:7-alpine

  api:
    build: ./api
    depends_on:
      - db
      - redis
    environment:
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/myapp
      REDIS_URL: redis://redis:6379

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - api

volumes:
  pgdata:

Common Commands

docker compose up              # create and start
docker compose up -d           # in background
docker compose down            # stop and remove
docker compose down -v         # remove volumes too

docker compose logs            # view logs
docker compose logs -f api     # follow logs for a service

docker compose ps              # list running services
docker compose exec api bash   # shell into a service

docker compose build           # rebuild images
docker compose pull            # pull latest images

docker compose restart         # restart all services
docker compose restart api     # restart a single service

Multiple Environments

docker compose -f docker-compose.yml -f docker-compose.prod.yml up
# docker-compose.override.yml (auto-loaded for development)
services:
  api:
    volumes:
      - ./api:/app
    environment:
      - DEBUG=1
# docker-compose.prod.yml
services:
  api:
    restart: always
    environment:
      - DEBUG=0

Networks

services:
  api:
    networks:
      - frontend
      - backend
  db:
    networks:
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

Best Practices

  1. Use .env files — never hardcode secrets in docker-compose.yml
  2. Specify restart policiesunless-stopped for production
  3. Use healthchecks — ensures services are actually ready
  4. Pin image versionspostgres:16-alpine, not postgres:latest
  5. Keep dev and prod close — minimize differences between environments
  6. Use .dockerignore — don’t send unnecessary files to the builder

Related: Start with Docker for beginners and learn Dockerfile best practices.