Overview
“The Compose file is invalid” means your compose.yaml (or docker-compose.yml) violates the Compose file schema: wrong keys, wrong types, bad indentation, or unsupported features. This guide shows quick checks, a minimal working example, and concrete fixes.
Applies to Docker Compose V2 (docker compose) and legacy V1 (docker-compose). V2 ignores the version key and focuses on the Compose Specification.
Quickstart: fastest way to pinpoint issues
- Ensure you’re using Compose V2.
- Run:
docker compose version
- Run:
- Validate and render the full config (merges multiple -f files, resolves env vars):
docker compose config- If this fails, the error location is usually printed (path like services.web.ports[0]).
- If env substitution might be breaking types, try validation without interpolation:
docker compose config --no-interpolate - Lint YAML syntax (indentation, tabs):
yamllint compose.yaml - Reduce to a minimal file (below) and add sections back until the error reappears.
Minimal working example
A small, valid Compose file (no version key required):
# compose.yaml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
Run:
docker compose up -d
If this works in your environment, the issue is in your original file’s structure or keys.
Step-by-step diagnosis
Confirm file names and location
- Compose auto-detects compose.yaml, compose.yml, docker-compose.yaml, or docker-compose.yml in the current directory. Otherwise pass -f.
docker compose -f ./path/to/compose.yaml configCheck YAML syntax first
- Replace tabs with spaces; YAML forbids tabs for indentation.
- Ensure lists use dashes and maps use key: value aligned properly.
Inspect the error path
- Errors often show a path like
services.api.deployorservices.web.ports[0]. - Verify those keys against the Compose spec expectations (types and allowed fields).
- Errors often show a path like
Validate with and without env interpolation
- Bad or missing env vars can turn numbers/booleans into invalid types.
docker compose config docker compose config --no-interpolateIsolate by commenting
- Comment out blocks (volumes, networks, deploy) until validation passes; then narrow down.
Check for unknown or misplaced keys
- Top-level must be: services, volumes, networks, configs, secrets, name, profiles.
- Inside a service, ensure keys like image, build, ports, environment, depends_on, volumes, healthcheck are correctly nested.
Verify list vs map forms
- Some sections support both list and map. Use the documented shape consistently.
Update tooling if needed
- Newer Compose features sometimes require a newer Docker/Compose plugin. Check
docker compose versionand update if very old.
- Newer Compose features sometimes require a newer Docker/Compose plugin. Check
Common causes and concrete fixes
| Symptom or message | Likely cause | Fix |
|---|---|---|
| Additional property xyz is not allowed | Unknown key at a level | Remove or move the key to the right level; verify spelling. |
| services is required | Missing top-level services | Add services: and indent service definitions under it. |
| services.web.ports must be a list | Wrong type (map or scalar) | Use a YAML list: ports:\n - "8080:80". |
| services.web.ports[0] invalid type | Non-string port mapping shape | Quote short syntax: "8080:80", or use long syntax object with target/published. |
| services.app.environment must be a mapping or list | Wrong env format | Use mapping: environment:\n FOO: "bar" or list: - FOO=bar. |
| cannot load env file | Bad --env-file path | Ensure file exists and uses KEY=VALUE lines, no quotes. |
| Unknown top-level key: service | Typo | Use services: (plural). |
| Unsupported option: deploy (in local Compose) | Swarm-only fields | Remove deploy: for local Compose or accept it will be ignored. |
| Invalid interpolation format | ${VAR} issues | Provide defaults "${VAR:-value}" or set the variable. |
| mapping values are not allowed here | YAML indentation/tab issue | Replace tabs, fix colon alignment, wrap strings with special characters in quotes. |
Correct long syntax examples
- Ports long syntax:
services:
web:
image: nginx:alpine
ports:
- target: 80
published: 8080
protocol: tcp
- Environment mapping:
services:
api:
image: myapp:latest
environment:
APP_ENV: "production"
WORKERS: "4"
- Volumes: bind vs named
services:
db:
image: postgres:16
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
Pitfalls to avoid
- Mixing tabs and spaces. Use 2 spaces consistently.
- Misplaced keys:
portsbelongs under a service, not top-level. - Unquoted values that YAML coerces (on/off, yes/no, 01) into booleans or octal. Quote them.
- Env substitution yielding empty strings that break types (e.g.,
published: "${PORT}"where PORT is unset). Provide defaults:${PORT:-8080}. - Using
container_portorhost_portkeys—these are not valid. Useportsshort or long syntax. - Declaring
volumes:as a list of strings at top-level when you need driver/options. Use a map when adding options. - Relying on
deployfor local Compose behavior. Deploy is for Swarm; Compose may ignore it or flag it. - File encoding/line endings issues on Windows. Prefer UTF-8 with LF; use a proper editor or
dos2unix.
Performance notes
- Large Compose files validate slower. Break into multiple files and use
-f base.yaml -f overrides.yamlto keep config readable and maintainable. - Reduce env interpolation overhead during validation with
docker compose config --no-interpolatewhen debugging. - Prefer maps for top-level
volumesandnetworksif you need options; this avoids repeated long lists and speeds edits. - Avoid excessive anchors/aliases for critical sections if teammates’ editors mis-handle them; readability reduces error-prone edits.
- Keep images pinned to tags/digests to reduce unexpected changes that trigger cascading edits.
Tiny FAQ
Do I need the version key?
- No. Compose V2 ignores
version. Omit it to avoid confusion.
- No. Compose V2 ignores
Which file name should I use?
- Prefer compose.yaml. docker-compose.yml is still recognized.
How do I validate without starting containers?
- Use
docker compose config(and--no-interpolateif needed). You can also rundocker compose up --dry-runto check planning without changes.
- Use
Why does
deploycause warnings?deployis for Swarm. Local Compose ignores much of it and might flag options as unsupported.
How do I pass env files safely?
- Use
--env-file .envorenv_file:in a service. EnsureKEY=VALUElines, no quotes or spaces around=.
- Use
Summary checklist
- [ ] File named correctly (compose.yaml) or passed via -f
- [ ] Valid YAML (no tabs, correct indentation)
- [ ] Top-level keys: services, volumes, networks, etc.
- [ ] Service keys valid and correctly typed
- [ ] Env vars resolved (or defaults provided)
- [ ]
docker compose configpasses beforeup