KhueApps
Home/DevOps/Fixing 'did not find expected key' in docker-compose.yml

Fixing 'did not find expected key' in docker-compose.yml

Last updated: October 07, 2025

What this error means

The message yaml: did not find expected key is a YAML parsing error. Docker Compose could not interpret your docker-compose.yml because of malformed YAML. This is usually due to indentation, missing colons, stray characters, or invalid list/map structure—not Docker-specific logic.

Quickstart: fix it fast

  1. Validate syntax:
    • Run:
      docker compose config
      
      This prints the normalized config or fails with a clearer location.
  2. Show line numbers when opening the file so the reported line is obvious.
  3. Replace tabs with spaces (YAML forbids tabs for indentation):
    grep -n $'\t' docker-compose.yml || true
    expand -t 2 docker-compose.yml > /tmp/compose.yml && mv /tmp/compose.yml docker-compose.yml
    
  4. Ensure every mapping key has a colon and value (e.g., image: nginx:1.25).
  5. Check lists under ports, volumes, environment, command for correct indentation.
  6. Quote values containing colons, hash (#), or special characters.
  7. Re-run docker compose config until it succeeds.

Minimal working example

Use this to compare structure and spacing (2 spaces per indent, no tabs):

services:
  web:
    image: nginx:1.25
    ports:
      - "8080:80"
    environment:
      - NGINX_HOST=localhost
      - NGINX_PORT=80
    volumes:
      - ./html:/usr/share/nginx/html:ro
    command: ["nginx", "-g", "daemon off;"]

Notes:

  • No top-level version key is required in modern Compose.
  • Quotes around "8080:80" avoid ambiguity with the colon.

Common causes and fixes

SymptomExample (bad)Fix
Tabs used for indentTabs before keysReplace tabs with spaces (2 or 4 spaces consistently)
Missing colon after keyimage nginx:1.25image: nginx:1.25
Misindented list itemsports:\n- "8080:80"ports:\n - "8080:80"
Unquoted value with coloncommand: python -m http.server:8000command: "python -m http.server:8000" or list form
Dangling dashenvironment:\n -Remove the dangling dash or provide a value
Mixed list/map stylesenvironment: NGINX_HOST=localhostUse list or map, not a bare string
Inline comments in the wrong placeimage: nginx:1.25 #tag: latestKeep comments away from ambiguous colons or quote the value

Indentation and tabs

Bad:

services:
\tweb:
    image: nginx:1.25

Good:

services:
  web:
    image: nginx:1.25

Missing colons

Bad:

services:
  web
    image: nginx:1.25

Good:

services:
  web:
    image: nginx:1.25

Lists under ports/volumes/environment

Bad:

services:
  api:
    image: my/api
    ports:
    - "8080:80"

Good:

services:
  api:
    image: my/api
    ports:
      - "8080:80"

Values with colons or special characters

Bad (plain scalar with colon can confuse parser depending on context):

services:
  app:
    command: python -m http.server:8000

Good (quote or use list):

services:
  app:
    command: "python -m http.server:8000"
# or
services:
  app:
    command: ["python", "-m", "http.server", "8000"]

environment syntax

All of these are valid; mixing them incorrectly is not.

Map style:

environment:
  APP_ENV: production
  LOG_LEVEL: info

List style:

environment:
  - APP_ENV=production
  - LOG_LEVEL=info

Bad (bare string):

environment: APP_ENV=production

Multiline commands and entrypoints

Use YAML block scalars or arrays, with correct indentation.

Bad:

command:
- |
  npm run start
  --port 3000

Good:

command: |
  npm run start \
    --port 3000
# or
command: ["npm", "run", "start", "--", "--port", "3000"]

Top-level sections ordering

Compose expects known top-level keys (services, networks, volumes). Unknown keys or misplacement won’t typically cause this exact error, but bad indentation around them will. Keep services at the root and define networks/volumes at the root as needed.

Step-by-step debugging procedure

  1. Run docker compose config to get a precise error location.
  2. Open the file with visible whitespace; replace tabs with spaces.
  3. Validate YAML structure:
    • Top-level: services
    • Next level: service names (web, api)
    • Under each service: keys like image, build, ports, environment, volumes
  4. Check each list:
    • All list items start with - aligned under their parent key.
    • No trailing dashes.
  5. Check each mapping:
    • Every key has a colon and a value, even if empty (key: "").
  6. Quote risky scalars:
    • Anything with :, #, {, }, [, ], ,, &, *, ?, |, >, @, or leading/trailing spaces.
  7. Minimize to isolate:
    • Comment out sections until the error disappears, then focus on the last change.
  8. Rebuild the normalized config:
    docker compose -f docker-compose.yml config
    
  9. If using multiple files, test them individually, then merge:
    docker compose -f docker-compose.yml -f docker-compose.override.yml config
    

Performance notes (compose workflows)

  • Keep build contexts small using .dockerignore to speed up docker compose build.
  • Prefer multi-stage builds to reduce image size and startup time.
  • Avoid unnecessary bind mounts of large directories; mount only what you need.
  • Use named volumes for dependency caches (e.g., package managers) to speed rebuilds.
  • Limit service logs during development (e.g., logging options) to reduce I/O.
  • Use depends_on only where needed; unnecessary service startups slow feedback loops.

Tiny FAQ

  • Why does docker compose config help? It parses and renders your YAML, catching syntax errors and showing the merged result across multiple files.

  • Do I need a version key? Not in modern Compose. The Compose Specification does not require version; services at the root are enough.

  • Can comments break YAML? Comments starting with # are fine, but if you put a colon-like pattern inside an unquoted value next to a comment, quoting the value is safer.

  • Does indentation size matter? Any consistent number of spaces works. Two spaces per level is common; tabs are not allowed.

  • How do I find tabs quickly? Use grep -n $'\t' docker-compose.yml and replace with spaces.

Series: Docker

DevOps