Generators8 min read

Docker Compose Guide: Syntax, Examples, and Common Patterns

Learn Docker Compose file syntax field by field. Covers services, networks, volumes, environment variables, health checks, and real-world multi-container patterns.

Try the free online tool mentioned in this guide:Docker Compose Generator

What is Docker Compose?

Docker Compose is a tool for defining and running multi-container applications from a single YAML file (docker-compose.yml or compose.yml). Instead of running multiple docker run commands with ports, volumes, networks, and environment variables, you declare all services in one file and start everything with docker compose up.

Compose is the standard for local development environments: spin up a web app, database, cache, and message broker with one command, and tear them all down with one command.

Basic docker-compose.yml structure

A Compose file has four top-level keys: services, networks, volumes, and configs. The most used is services.

yaml
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./static:/usr/share/nginx/html:ro
    depends_on:
      - api

  api:
    build: ./api          # build from local Dockerfile
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: myapp
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  db_data:

Services: the core building block

Each key under services is a container definition. Key fields:

  • image: Docker Hub image name and tag.
  • build: path to a directory with a Dockerfile, or a build config object.
  • ports: host:container port mapping. "8080:80" exposes container port 80 on host port 8080.
  • environment: env vars as a list (- KEY=value) or map (KEY: value).
  • volumes: bind mounts (./local:container) or named volumes.
  • depends_on: start order. With condition: service_healthy, waits for the healthcheck.
  • restart: no (default), always, on-failure, unless-stopped.

Environment variables and .env files

Compose automatically reads a .env file in the same directory and substitutes ${VARIABLE} references in the Compose file. Never hard-code secrets in docker-compose.yml committed to git.

yaml
# .env file (never commit to git)
POSTGRES_PASSWORD=supersecret
API_SECRET_KEY=myapikey

# docker-compose.yml references
services:
  db:
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
  api:
    environment:
      SECRET_KEY: ${API_SECRET_KEY}

Networks: service-to-service communication

By default, Compose creates a single default network and all services join it. Services reach each other by service name — api can connect to db using the hostname db.

For isolation, define custom networks. A service can join multiple networks.

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

networks:
  frontend:
  backend:
    internal: true   # not reachable from host

Common real-world patterns

Web + Database: nginx/Caddy as reverse proxy, Node/Python/Go API, PostgreSQL or MySQL. The API uses the DB service name as hostname.

Web + Cache: Same as above plus Redis as the cache service. The API connects to redis:6379.

Full stack: Frontend (React/Next.js), backend API, database, Redis, background worker (Celery/BullMQ) — all defined as separate services with shared networks and a volume for the database.

Overrides: Use docker-compose.override.yml for local dev customizations (bind mounts, debug ports) without modifying the base file.

Essential Compose commands

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

# Start and rebuild images
docker compose up -d --build

# Stop and remove containers (keep volumes)
docker compose down

# Stop and remove containers + volumes
docker compose down -v

# View logs (follow)
docker compose logs -f api

# Run a one-off command in a service container
docker compose exec api sh

# Scale a service to 3 replicas
docker compose up -d --scale worker=3

Frequently asked questions

What is the difference between docker-compose and docker compose?

docker-compose (with a hyphen) is the older standalone Python binary (Compose v1). docker compose (space) is the newer Go plugin bundled with Docker Desktop and Docker Engine 20.10+ (Compose v2). Compose v2 is the current standard; v1 is deprecated.

How do I make one service wait for another to be ready?

Use depends_on with condition: service_healthy, and add a healthcheck to the dependency. Without a healthcheck, depends_on only waits for the container to start, not for the service inside to be ready.

Should I use Docker Compose in production?

Compose is designed for local development and single-host deployments. For multi-host production workloads, use Kubernetes, Docker Swarm, or a managed container service (ECS, Cloud Run). Compose is fine for small self-hosted apps on a single server.

What is the difference between volumes and bind mounts in Compose?

Named volumes (db_data:) are managed by Docker and persist data between container restarts independent of host paths. Bind mounts (./local:/container) sync a host directory into the container — used for live code reloading in development.

Try Docker Compose Generator for free

Pick PostgreSQL, Redis, NGINX, Kafka, Elasticsearch, Prometheus, Grafana, and dozens of other images — get a ready-to-edit docker-compose.yml for local development. No install, no account required to try it.