Quick Reference · multi-container orchestration

docker compose cheat sheet

One YAML file, one CLI. Docker Compose reads compose.yaml, builds/pulls images, creates isolated networks, mounts volumes, and starts every service in the right order — so you never manage containers one by one.

CLI commands service / build environment volumes networks deps / health config / file production note most common

Distilled & cross-checked across: dockerlabs.collabnix.com · devhints.io/docker-compose · devopscycle.com · gist/jonlabelle · helgeklein.com · docs.docker.com/compose

How Docker Compose works — one file drives the whole stack
compose.yaml services: volumes: networks: configs: / secrets: reads docker compose CLI / V2 plugin up · down · logs · exec build · ps · restart calls Docker Engine builds images creates containers manages net + vol BRIDGE NETWORK (default or custom) web port 80:80 build: ./ api port 3000:3000 depends_on: db db image: postgres vol: pgdata:/var/lib/ pgdata named volume HOST :80 :3000 ports:
01Core CLI Commandsthe daily loop
02Status & Inspectobserve & debug
03Exec & Runget inside containers
04File Anatomytop-level keys
05Build & Imagewhere the image comes from
06Ports & Exposehost ↔ container
07Environment Variablesconfigure at runtime
08Volumespersist data
09Networksinter-service communication
10Start Order & deps_onstartup sequencing
11Health Checksservice readiness
12Restart Policiesauto-recovery
13Commands & Entrypointoverride what runs
14Advanced Optionsper-service extras
One-Service Referenceeverything in context

Container lifecycle & network topology

Two mental models that unlock everything else in Compose.

Container lifecycle — states & transitions

Compose commands move containers between these states. up is the on-ramp; down is the off-ramp.

created running paused stopped removed compose up pause unpause stop start rm down

Network topology — bridge, isolation & DNS

Services on the same network reach each other by service name. Services on different networks are isolated — even in the same Compose file.

frontend network backend network web :80:80 api :3000 (both nets) db postgres http://api db:5432 web ✗ cannot reach db (different networks) HOST :80 ports:

compose.yaml — annotated skeleton

The six top-level keys and how they wire together — everything else is a property inside one of these.

# compose.yaml  (replaces docker-compose.yml in Compose V2)

services:                          # ← one key per container
  web:
    build: .                    # or  image: nginx:latest
    ports:  ["80:80"]
    environment:
      NODE_ENV: production
    env_file: [.env]
    volumes:  ["./src:/app:ro"]
    networks: [frontend]
    restart:  unless-stopped
    depends_on:
      db: {condition: service_healthy}
    healthcheck:
      test: ["CMD","curl","-f","http://localhost"]
      interval: 10s  timeout: 5s  retries: 3

  db:
    image:  postgres:16
    volumes: [pgdata:/var/lib/postgresql/data]
    networks: [frontend]
    restart:  unless-stopped
    healthcheck:
      test: ["CMD","pg_isready","-U","admin"]
      interval: 5s  start_period: 10s

volumes:                          # ← top-level volume declarations
  pgdata:                         # Docker manages the path

networks:                         # ← top-level network declarations
  frontend:                       # empty = use Docker defaults

Worth memorizing

.env ≠ env_file:.env feeds Compose substitution; env_file: feeds the container
expose ≠ ports:expose is docs — containers already see each other; ports opens to the host
depends_on ≠ ready:add a healthcheck + condition: service_healthy to actually wait
down ≠ down -v:plain down preserves volumes; -v nukes named volumes too
version: keydeprecated in V2 — omit it completely from new files
named vol vs bind:named = Docker manages path; bind = you pick the host path
unless-stoppedbest restart policy — survives daemon restart, respects manual stop
service name = DNS:curl http://db:5432 works inside any container on the same network
docker compose configprints the fully merged file — your first debugging step
run --rmone-off task (migration, seed) — container is deleted on exit