DevTools Hub

Search tools

Search for a developer tool

Common Docker Compose Errors

Part of the Docker Toolkit

A docker-compose.yml file can be perfectly valid YAML and still be a broken Compose file — the two are different questions, and the gap between them is exactly where these mistakes live. YAML syntax errors get caught immediately; a Compose-specific mistake usually waits until docker compose up to surface, sometimes with an error that doesn't obviously point back at the actual line. Every example below was run against a real Docker Compose install rather than guessed at.

The "boolean-coercion trap" that no longer exists

environment:
  DEBUG: no
  COUNTRY: NO

Older YAML tooling (and older Compose implementations) followed the YAML 1.1 spec, where an unquoted yes, no, on, or off is read as a real boolean — DEBUG: no silently becoming false, or worse, COUNTRY: NO (Norway's ISO country code) becoming false too. This is well-known enough to have its own name, the "Norway problem," and it gets repeated in a lot of still-circulating Compose advice. Verified against a real, current Docker Compose install (v5.2.0) with docker compose config --format json: it doesn't reproduce. DEBUG: no, COUNTRY: NO, and even on/off/true/false inside an untyped x- extension field all come back as plain strings, not booleans — current Compose (compose-go) no longer applies YAML 1.1 boolean resolution. Worth knowing which way the myth runs before quoting every value defensively out of habit.

version: silently loses precision

version: 3.10

An unquoted version: value is parsed as a YAML number, not a version string — verified: 3.10 parses to the number 3.1, dropping the trailing zero the same way any numeric literal would. It mostly doesn't matter in practice for a different reason: the version: key is obsolete in the current Compose Specification and Docker Compose v2 ignores it outright. Safe to delete rather than fix.

Malformed port mappings

ports:
  - "3000:3000:3000:extra"
  - "99999:80"

Two different failure shapes, both verified against a real Compose install. A port number outside 1-65535 (a typo'd extra digit is the common cause) fails clearly enough: host port "99999" is not a valid port. The extra-segments case is the one worth knowing in advance, because the real error doesn't describe the actual problem — Compose supports an optional ip:host:container form, so with too many :-separated parts it tries reading the extra piece as an IP address instead and fails with invalid IP address: 3000:3000, not anything mentioning "too many segments." Both are silent until Compose actually parses the port — the YAML itself is fine, since a port mapping is just a string as far as YAML is concerned.

Referencing an undeclared named volume

services:
  db:
    image: postgres:16
    volumes:
      - data:/var/lib/postgresql/data
# no top-level "volumes:" section declaring "data"

A service-level volume mount like data:/var/lib/postgresql/data only counts as a bind mount if data looks like a path (starts with ./, /, or similar). Otherwise Compose treats it as a reference to a named volume, which has to be declared in a top-level volumes: section — without one, a real Compose install rejects this with service "db" refers to undefined volume data: invalid compose project. The fix is either adding the top-level declaration or, if a bind mount to a local directory was actually intended, prefixing it with ./.

Referencing an undeclared network

Same shape as the volume case: a service listing a network under its own networks: key needs that network declared at the top level too, unless it's the implicit default network Compose creates automatically. A renamed or removed top-level network entry that a service still references is an easy thing to miss in a large file.

depends_on pointing at an undefined service

services:
  api:
    image: myapi
    depends_on:
      - db
# no service actually named "db"

A typo'd or renamed service name in depends_on — a real Compose install rejects this with service "api" depends on undefined service "db": invalid compose project. Worth checking for the reverse mistake too: a service listing itself in its own depends_on, which is equally invalid. Neither is a YAML problem, which is exactly why a plain YAML linter won't catch it — both are valid YAML, just meaningless Compose.

A service with no image, build, or extends

services:
  web:
    ports:
      - "80:80"
# missing "image:" or "build:"

Compose needs to know what to actually run — every service needs one of image, build, or extends. A real Compose install rejects the example above with service "web" has neither an image nor a build context specified: invalid compose project. Easy to lose during a refactor: deleting an image: line while restructuring a service to build locally, without adding the corresponding build: section.

A volume entry missing its target path

volumes:
  - ./config:

The short-form volume syntax is source:target (optionally with a third :mode segment); a trailing colon with nothing after it leaves the target path empty, which is a rejected volume entry, not an assumed default.

An unrecognized restart policy

restart: always-unless-stopped

Compose only recognizes no, always, on-failure (optionally with a max retry count, on-failure:5), and unless-stopped. Verified against a real Compose install: docker compose config accepts the invented value above without any error or warning at all — it's passed straight through as an opaque string, silently doing nothing rather than restarting the container on any policy. Worth double-checking against the exact accepted values rather than assuming a reasonable-looking string works, since nothing will tell you it didn't.

Try it yourself

Docker Compose Validator checks for most of the mistakes above directly — undeclared volumes/networks, undefined depends_on references, malformed ports, a missing image/build — before you ever run docker compose up. Docker Compose Formatter cleans up indentation while preserving comments, and Docker Compose Visualizer turns the file into a diagram of service start order, networks, and volumes — useful for spotting a missing reference visually before it becomes a validator error. For the Dockerfile side and the depends_on-doesn't-mean-ready gotcha, see Docker Best Practices.

Related tools