Overview

Docker Compose defines multi-container applications in a single YAML file. This cheat sheet covers the structure of compose.yaml, every service key, environment and configuration options, networking and volumes, the full CLI, override files, and profiles. All examples target Docker Compose v2 (the docker compose plugin) and the Compose Specification; the legacy version: key is obsolete and ignored.

compose.yaml structure

The basic structure of a Compose file:

services:      # Define containerized services
volumes:       # Define named volumes
networks:      # Define custom networks
configs:       # Define configs (for swarm, optional)
secrets:       # Define secrets (for swarm, optional)

Services: basic keys

Key Purpose Example
image: <name>:<tag> Use a pre-built image image: nginx:1.27
build: <path> Build an image from a Dockerfile in the given path build: . or build: ./services/web
build: { context: <path>, dockerfile: <file> } Build with explicit context and Dockerfile path build: { context: ., dockerfile: Dockerfile.prod }
build: { context: <path>, args: { VAR: value } } Pass build args build: { context: ., args: { NODE_ENV: production } }
container_name: <name> Set the container name (not recommended for scaling) container_name: my_web_app
restart: <policy> Restart policy: no, always, on-failure, unless-stopped restart: unless-stopped
restart: on-failure:<max-retries> Restart on failure up to N times restart: on-failure:5

Networking and ports

Key Purpose Example
ports: [<host>:<container>] Map container port to host ports: ["8080:80"]
ports: [<host-ip>:<host>:<container>] Map to specific host IP ports: ["127.0.0.1:8080:80"]
ports: [<container>] Expose without mapping to host ports: ["8080"]
expose: [<port>] Expose port for internal communication (no host mapping) expose: ["8080"]
networks: [<network>] Attach service to custom networks networks: [frontend, backend]
networks: <network>: { ipv4_address: <ip> } Assign static IP networks: { backend: { ipv4_address: 172.20.0.2 } }
hostname: <name> Set container hostname hostname: db-server
links: [<service>] Link to another service (legacy, use networks instead) links: [db]
dns: [<ip>] Set DNS servers dns: [8.8.8.8, 8.8.4.4]
dns_search: [<domain>] Set DNS search domains dns_search: [example.com]

Environment and configuration

Key Purpose Example
environment: { VAR: value } Set environment variables inline environment: { DEBUG: "true", LOG_LEVEL: info }
environment: [VAR=value] Set environment variables as a list environment: [DEBUG=true, LOG_LEVEL=info]
env_file: <file> Load environment variables from a file env_file: .env
env_file: [<file1>, <file2>] Load from multiple files (later files override earlier ones) env_file: [.env, .env.local]
working_dir: <path> Set working directory inside the container working_dir: /app
command: <command> Override the CMD from Dockerfile command: python -u app.py
command: [<cmd>, <arg1>, <arg2>] Command as a list (exec form, preferred) command: [python, -u, app.py]
entrypoint: <cmd> Override ENTRYPOINT from Dockerfile entrypoint: /bin/bash
entrypoint: [<cmd>, <arg>] ENTRYPOINT as a list (exec form, preferred) entrypoint: [python, -u]
stdin_open: true Keep stdin open even if not attached stdin_open: true
tty: true Allocate a pseudo-terminal tty: true

Storage: volumes and bind mounts

Key Purpose Example
volumes: [<host>:<container>] Bind mount a host directory volumes: [./data:/app/data]
volumes: [<name>:<container>] Mount a named volume volumes: [my_data:/app/data]
volumes: { <container>: {} } Anonymous volume volumes: { /app/data: {} }
volumes: { <container>: { ro: true } } Read-only mount volumes: { /data: { ro: true } }
volumes: { <container>: { consistency: <mode> } } Consistency mode (macOS/Windows only) volumes: { /data: { consistency: delegated } }
tmpfs: [<path>] Mount a temporary filesystem tmpfs: [/tmp]

Note: Define named volumes at the top level:

volumes:
  my_data:
    driver: local

Resource limits

Key Purpose Example
cpus: <float> Limit CPU cores (e.g., 0.5 for half a core) cpus: 2
cpu_shares: <int> CPU share weight (relative priority) cpu_shares: 512
mem_limit: <value> Limit memory (e.g., 512m, 1g) mem_limit: 512m
memswap_limit: <value> Limit memory + swap memswap_limit: 1g
mem_reservation: <value> Soft memory limit (allows exceeding if space available) mem_reservation: 256m

Health checks

Key Purpose Example
healthcheck: { test: [CMD, curl, -f, 'http://localhost/health'] } Define a health check command (see below)
healthcheck: { test: CMD curl -f http://localhost/health } Health check as a string healthcheck: { test: CMD curl -f http://localhost/health }
healthcheck: { test: CMD-SHELL, 'pg_isready -U postgres' } Health check with shell (see below)
healthcheck: { interval: 30s } How often to run the check (default: 30s) interval: 30s
healthcheck: { timeout: 5s } Timeout for the check (default: 30s) timeout: 5s
healthcheck: { start_period: 10s } Grace period before first check (default: 0s) start_period: 10s
healthcheck: { retries: 3 } Consecutive failures before marking unhealthy (default: 3) retries: 3
healthcheck: disable: true Disable health check (if inherited from image) disable: true

Full example:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
  interval: 30s
  timeout: 5s
  start_period: 10s
  retries: 3

Dependencies and startup order

Key Purpose Example
depends_on: [<service>] Wait for services to start (no readiness check) depends_on: [db, cache]
depends_on: { <service>: { condition: service_started } } Wait for service to start depends_on: { db: { condition: service_started } }
depends_on: { <service>: { condition: service_healthy } } Wait for service to be healthy depends_on: { db: { condition: service_healthy } }
depends_on: { <service>: { condition: service_completed_successfully } } Wait for service to complete successfully (one-off jobs) depends_on: { migration: { condition: service_completed_successfully } }

Caution: condition: service_healthy requires the service to have a healthcheck. Without it, the condition fails immediately.

Full example:

services:
  web:
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started

Security: secrets and configs

Key Purpose Example
secrets: [<name>] Mount a secret file in /run/secrets/<name> secrets: [db_password]
configs: [<name>] Mount a config file in / configs: [nginx_config]

Top-level definition:

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

configs:
  nginx_config:
    file: ./nginx.conf

Access in container:

# In shell: cat /run/secrets/db_password

Profiles

Profiles allow you to conditionally include services.

services:
  web:
    image: nginx:latest
    # Always included

  debug:
    image: busybox:latest
    profiles: [debug]
    # Only included with --profile debug

  monitoring:
    image: prom/prometheus:latest
    profiles: [monitoring, prod]
    # Included with --profile monitoring OR --profile prod

Usage:

docker compose up                        # Include only services without profiles
docker compose --profile debug up        # Include web + debug
docker compose --profile monitoring up   # Include web + monitoring
docker compose --profile debug --profile monitoring up  # Include all three

Networks (top-level)

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br0

Service usage:

services:
  web:
    networks: [frontend, backend]
  db:
    networks: [backend]

Volumes (top-level)

volumes:
  db_data:
    driver: local
  shared_cache:
    driver: local
    driver_opts:
      type: tmpfs
      device: tmpfs

Override files

Docker Compose automatically loads docker-compose.override.yaml and merges it with compose.yaml.

compose.yaml (production):

services:
  web:
    image: myapp:latest
    environment:
      LOG_LEVEL: warn

docker-compose.override.yaml (local development):

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      LOG_LEVEL: debug

Result when running docker compose up:
– Image is built from ./Dockerfile (overrides image: myapp:latest)
– Port 8000 is exposed on the host
– LOG_LEVEL is set to debug
– Port mapping and LOG_LEVEL are merged

Explicit override file:

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

CLI commands

Command Purpose Example
docker compose up Create and start all services docker compose up
docker compose up -d Start services in detached mode (background) docker compose up -d
docker compose up -d --scale web=3 Scale a service to N replicas docker compose up -d --scale web=3
docker compose down Stop and remove all services, networks (keeps volumes) docker compose down
docker compose down -v Stop and remove services, networks, AND volumes docker compose down -v
docker compose down --rmi all Remove services and delete all images docker compose down --rmi all
docker compose down --remove-orphans Remove services not in the Compose file docker compose down --remove-orphans
docker compose ps List running services docker compose ps
docker compose logs View logs from all services docker compose logs
docker compose logs -f Stream logs continuously (follow) docker compose logs -f
docker compose logs -f <service> Stream logs from one service docker compose logs -f web
docker compose logs --tail 50 <service> Show last 50 lines from a service docker compose logs --tail 50 web
docker compose exec <service> <command> Run a command inside a service docker compose exec web bash
docker compose exec -it <service> bash Run bash interactively inside a service docker compose exec -it db psql -U postgres
docker compose exec -u <user> <service> <command> Run as a specific user docker compose exec -u appuser web whoami
docker compose pull Pull the latest images for all services docker compose pull
docker compose build Build all images (if build: is specified) docker compose build
docker compose build --no-cache <service> Rebuild without cache docker compose build --no-cache web
docker compose start Start stopped services docker compose start
docker compose stop Stop running services docker compose stop
docker compose stop <service> Stop a specific service docker compose stop web
docker compose restart Restart all services docker compose restart
docker compose restart <service> Restart a specific service docker compose restart web
docker compose pause Pause all services docker compose pause
docker compose unpause Resume paused services docker compose unpause
docker compose config Validate and print the final Compose config docker compose config
docker compose config --resolve-image-digests Resolve image digests (useful for reproducibility) docker compose config --resolve-image-digests
docker compose watch Watch for changes and sync/rebuild services (Compose 2.22+) docker compose watch
docker compose version Show Docker Compose version docker compose version
docker compose -f <file> <command> Explicitly specify a Compose file docker compose -f docker-compose.prod.yaml up
docker compose --project-name <name> <command> Use a custom project name (default: directory name) docker compose --project-name myapp up

Patterns you’ll reuse

# Development with override file
# compose.yaml: production setup
# docker-compose.override.yaml: local dev tweaks

# Database with healthcheck and depends_on
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
  web:
    depends_on:
      db:
        condition: service_healthy

# Multi-tier application
services:
  web:
    networks: [frontend, backend]
  cache:
    networks: [backend]
  db:
    networks: [backend]

# Load environment from file with overrides
services:
  app:
    env_file: .env
    environment:
      LOG_LEVEL: debug  # Override from .env

# Profiles for optional services
  monitoring:
    profiles: [monitoring]
  debug:
    profiles: [debug]

# Use compose watch for live reload (Compose 2.22+)
# docker compose watch
watch:
  - path: ./src
    action: sync
    target: /app/src
  - path: ./requirements.txt
    action: rebuild

FAQ

What’s the difference between docker-compose.yml and compose.yaml?

Both are supported. Docker prefers compose.yaml (no dash). Both are equivalent; pick one convention and stick with it. Modern documentation uses compose.yaml.

How do I set environment variables in Docker Compose?

Use the environment key in a service, or load from a file with env_file: .env. Variables can be hardcoded, from the host shell (${VAR_NAME}), or from .env files. Use secrets for sensitive values.

How do I ensure one service starts before another?

Use depends_on: db: condition: service_healthy (requires a HEALTHCHECK). For just startup order without validation, use the short form depends_on: [db]; it only orders startup and does not wait for readiness.

How do I use Docker Compose override files?

Create docker-compose.override.yaml in the same directory. Compose automatically loads it and merges it with compose.yaml. Override specific services without editing the main file. Useful for local development vs. production.

What are Docker Compose profiles?

Profiles allow you to conditionally include services. Set profiles: [debug] on a service, then use docker compose --profile debug up to include it. Useful for optional debugging services or monitoring stacks.

How this guide was verified

Written by hand from the official documentation, every command re-checked, and reviewed a second time before publishing. Last reviewed on .

Official sources read for this article

Share.

Haythem Rehouma builds and teaches DevOps and AI tooling: Kubernetes, Docker, Terraform, Claude Code, ChatGPT and n8n. Every KubPoint guide is written from the official docs and re-tested before it goes live.

Comments are closed.