Skip to main content
Docker Compose A single docker run with a pile of flags is hard to maintain, and multiple containers more so. Docker Compose declares the whole app (services, networks, volumes) in one compose.yaml and brings it all up with one command.
The current version is docker compose (v2, a space, no hyphen), built into the Docker CLI. The old docker-compose (the Python v1) is EOL, no longer maintained, do not use it. A compose.yaml also does not need and should not include a version: top-level field; it is obsolete in the Compose Specification and only produces an obsolescence warning.
Click the keys in compose.yaml to see what each block does:

Top-level structure

Common service keys

Creating networks

docker compose up automatically creates a project-specific bridge network (named <project>_default) and joins every service. A service name is its DNS hostname on that network, so web reaches the database at db:5432, no IP needed. To isolate tiers, declare your own networks:
proxy and db are on different networks and cannot talk directly; only app touches both.

Container group (project) name

Compose ties the set together with a “project name” and uses it as a naming prefix: Precedence (high to low): docker compose -p <name> > COMPOSE_PROJECT_NAME env var > top-level name: > the directory containing compose.yaml.

Environment configuration

Interpolation: ${VAR}, ${VAR:-default} (fall back when unset or empty), ${VAR:?msg} (error out when unset). In-container env var precedence (high to low): docker compose run -e > environment: > env_file: > host shell inheritance > Dockerfile ENV.

Using GPUs

The official way is to declare it under a service’s deploy.resources.reservations.devices (the host needs the NVIDIA Container Toolkit first):

Performance and startup order

  • depends_on with healthcheck: use condition: service_healthy to wait until a dependency is actually ready, so the app does not race a database that is not up yet.
  • Resource limits: deploy.resources.limits cpus / memory also take effect in standalone (non-Swarm) mode.
  • pull_policy and build cache: control whether to pull every time and reuse build cache.
  • Startup: up -d (background), --build (rebuild first), --scale web=3 (run 3 copies), --wait (wait until everything is healthy).

Volume and bind mount mapping

Compose’s volumes: handles both bind mounts and named volumes, distinguished by how the source is written (see Where data lives):
Use named volumes for data that must persist and bind mounts for live-syncing source. A path with no mount declared only writes to the container writable layer and is gone when the container is removed.

A complete compose.yaml

Common docker compose commands

The commands above all assume a default filename in the current directory. docker compose looks for compose.yaml (preferred), compose.yml, docker-compose.yaml, then docker-compose.yml, searching the working directory and walking up to parent directories. When the filename is not a default (for example docker-compose.dev.yml or my-stack.yaml), you must name it explicitly with -f. -f is a top-level option and goes before the subcommand (up / down …); multiple -f flags merge in order (later overrides earlier). The COMPOSE_FILE environment variable does the same. This is exactly how the official CLI docs define it.
Bringing a compose.yaml up looks like this:
docker compose down -v deletes named volumes (the ones declared in the volumes: section), and database data often lives there: once gone, it is gone. To only stop services and keep data, use docker compose down without -v.

Next

Reference: docs.docker.com/reference/compose-file, Compose GPU support