Skip to content
intermediate Phase 4 · Docker Compose

Compose Fundamentals

Write docker-compose.yml files and manage multi-container apps.

1h 15m
0 problems
Topic Progress 0%

Introduction to Docker Compose

Introduction to Docker Compose

Docker Compose is a tool for defining and running multi-container applications. Instead of running multiple docker run commands with complex flags, you define your entire application stack in a single docker-compose.yml file.

Why Compose?

Without Compose, running a full-stack app requires:

# Manual: error-prone, hard to repeat
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:16
docker run -d --name api --network appnet -e DB_HOST=db myapi
# Wait for db to be ready...
docker run -d --name web -p 80:80 --network appnet mywebapp

With Compose:

# One command does everything
docker compose up -d

docker-compose.yml Structure

services:
  web:
    image: nginx:1.25
    ports:
      - "80:80"
    depends_on:
      - api
    networks:
      - frontend

  api:
    build: ./api
    environment:
      - DB_HOST=db
      - DB_PASSWORD=secret
    depends_on:
      - db
    networks:
      - frontend
      - backend

  db:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - backend

volumes:
  pgdata:

networks:
  frontend:
  backend:

Compose File Versions

Docker Compose V2 (current) uses the services top-level key:

# V2 syntax (current)
services:
  web:
    image: nginx

# V1 syntax (deprecated)
version: "3.8"  # No longer needed in Compose V2
web:
  image: nginx

Basic Commands

# Start all services
docker compose up

# Start in background
docker compose up -d

# Stop all services
docker compose down

# Stop and remove volumes
docker compose down -v

# View running services
docker compose ps

# View logs
docker compose logs

# Follow logs
 docker compose logs -f

# Rebuild images
docker compose up --build

# Scale a service
docker compose up --scale web=3

Defining Services

Defining Services

Image vs Build

services:
  # Use pre-built image
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret

  # Build from Dockerfile
  api:
    build: ./api
    # Or with options:
    build:
      context: ./api
      dockerfile: Dockerfile.dev
      args:
        NODE_ENV: development
      cache_from:
        - myregistry.com/api:latest

  # Build with target stage
  worker:
    build:
      context: .
      dockerfile: Dockerfile
      target: builder

Ports

services:
  web:
    image: nginx
    ports:
      - "80:80"           # host:container
      - "443:443"
      - "127.0.0.1:8080:80"  # bind to localhost
      - "3000"            # random host port

Volumes

services:
  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data  # named volume
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro  # bind mount

  app:
    image: node:20
    volumes:
      - .:/app          # bind mount current dir
      - node_modules:/app/node_modules  # named volume for deps

volumes:
  pgdata:
  node_modules:

Environment Variables

services:
  api:
    image: myapi
    environment:
      - DB_HOST=db
      - DB_PORT=5432
    env_file:
      - .env
      - ./api/.env
    # Or map to host env vars:
    environment:
      DB_HOST: db
      DB_PASSWORD: ${DB_PASSWORD}

Complete Service Example

services:
  web:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    environment:
      - REACT_APP_API_URL=http://localhost:4000
    depends_on:
      - api
    networks:
      - frontend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3

Restart Policies

services:
  api:
    restart: no              # Never restart (default)
    restart: always          # Always restart
    restart: unless-stopped  # Restart unless explicitly stopped
    restart: on-failure:5    # Restart on failure, max 5 times

Compose Networking

Compose Networking

Automatic Network Creation

Compose creates a default network for your application:

# Starting compose creates a network named <project>_default
docker compose up -d

# Check the network
docker network ls | grep myproject
# myproject_default    bridge    local

All services in the same compose file automatically communicate by service name:

services:
  web:
    image: nginx
    # Can reach 'api' by name: http://api:4000
  api:
    image: myapi
    # Can reach 'db' by name: postgresql://db:5432
  db:
    image: postgres:16

Custom Networks

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

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # No internet access

Network Aliases

services:
  primary-db:
    image: postgres:16
    networks:
      app:
        aliases:
          - database
          - postgres

Port Publishing

services:
  web:
    image: nginx
    ports:
      # Publish to all interfaces
      - "80:80"
      # Publish to localhost only
      - "127.0.0.1:443:443"
      # UDP ports
      - "53:53/udp"
      # Host port range
      - "8000-8010:8000-8010"

Project Name

# Default: directory name
docker compose -p myproject up -d

# All resources get prefixed
# Network: myproject_default
# Volumes: myproject_pgdata
# Containers: myproject_web_1, myproject_api_1