Run docker compose up and you get service web has neither an image nor a build context — or worse, a silent startup where a typo in depends_on means the db pod never blocks the web pod. This validator parses your compose file in the browser and flags the common mistakes: missing image or build, wrong-shape ports, broken volume references, depends_on pointing at non-existent services, and environment blocks that mix list and map styles. Works on docker-compose.yml, compose.yaml, and rendered Helm output that targets the Compose Specification. Nothing is uploaded — paste a compose file with inlined secrets and the values stay local.
How to use
- Paste your compose file — drop in any
docker-compose.yml, compose.yaml, or rendered output. - Click Validate — the validator parses YAML and checks each service against common schema mistakes.
- Read the per-service report — errors include the offending service name and the specific field that failed.
- Fix and re-validate, then run
docker compose config locally for full spec compliance before docker compose up.
Example 1: Common bugs the validator catches
A typical newcomer mistake — neither image nor build, a broken depends_on, and a mistyped volume reference all in one file.
Compose with bugs
services:
web:
ports:
- "8080:80"
depends_on:
- database # service named "db", not "database"
volumes:
- assets:/var/www
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: s3cret
volumes:
asset: # typo — referenced as "assets"Validator output
web:
✗ neither "image" nor "build" defined
✗ depends_on references unknown service "database"
✗ volume "assets" not defined in top-level volumes:
volumes:
⚠ "asset" declared but never referenced
Example 2: depends_on shape trap
depends_on accepts two shapes — a simple list (compose v2) or a map with conditions (compose v3+). Mixing them silently breaks healthcheck-based ordering.
Old (list) form
services:
web:
image: nginx
depends_on:
- db
- cache
db:
image: postgres:16
cache:
image: redis:7Newer (map) form — waits for healthcheck
services:
web:
image: nginx
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7The list form only waits for the container to be created, not for the service inside to accept traffic. Use the map form with service_healthy when startup order matters — and define a real healthcheck on the upstream.
YAML specification reference
Quick reference from YAML 1.2. The converter enforces these rules on input; anything outside them is rejected or normalized.
| Element | Meaning | Example |
|---|
| Mapping | Block or flow key/value pairs | name: alice |
| Sequence | Ordered list with `- ` or flow `[...]` | - apple
- banana |
| Scalar | Plain, single-quoted, double-quoted, literal `|`, folded `>` | "hello\n" |
| Anchor / alias | & defines, * references; enables DRY configs | &base
*base |
| Tag | Explicit type: !!int, !!str, !!binary … | !!int "42" |
Common invalid forms
name:alice // missing space after colon
- item
-indented // inconsistent indent
flag: yes
value: 1 // tab used for indent (YAML forbids tabs)
Compose v2 vs v3 — what changed
| Topic | v2 | v3 |
|---|
Top-level version: | Required | Optional/ignored in Compose Spec (modern docker compose) |
depends_on healthcheck | List-only, no condition | Map with service_healthy condition |
deploy: block | Ignored (Swarm only) | Swarm; mostly ignored by docker compose locally |
| Long-form network config | Limited | Full driver options, IPAM, attachable |
| Compose Spec (2020+) | — | Replaces both — drop version: and use the unified spec |
Modern docker compose (the plugin) follows the Compose Specification — the old version: "3.x" string is ignored. Stick with the spec form for new files.
healthcheck and depends_on pitfalls
- depends_on does not wait for readiness by default. The list form only waits for container creation. Use
condition: service_healthy with a real healthcheck: on the upstream service. - healthcheck without start_period. Slow-booting services (Postgres, Elasticsearch, JVM apps) fail early healthchecks. Set
start_period: 30s(or longer) so initial failures don't mark the container unhealthy. - healthcheck running outside the container. The
test: command runs inside the container. curl localhost/health needs curl to be installed. Prefer CMD-SHELL with wget --quiet --tries=1 --spider ... on Alpine images. - Port collisions. Compose binds ports on the host. If two compose projects on the same host both want
5432, the second will fail to start without a clear error. Use specific host ports (5433:5432) or omit the host-side mapping when the port doesn't need to be public.
Errors this catches
- Service has neither
image nor build — one is required for every service entry. - Wrong port format — ports must be string (
"8080:80") or numeric, not nested objects without target/published. - Volume / volumes_from typos — references that do not match a defined top-level
volumes: key. - Broken
depends_on — service names that do not exist in the file. - Environment block shape — must be a list of
KEY=value strings or a map of KEY: value; mixing them silently breaks. - Top-level YAML errors — bad indentation, unclosed quotes, missing mapping colons.
YAML pitfalls specific to Compose files
- Tabs in indentation. YAML forbids tabs for indentation. The parser error reads
found character that cannot start any token. Configure your editor to insert 2 spaces for *.yml. - Bare numeric port strings.
ports: - 8080:80 vs ports: - "8080:80". Quoted is safer — bare can occasionally be parsed as a sexagesimal number in older YAML 1.1 parsers. - Bool-like strings. Image tags like
latest are fine, but tags such as off or yes (rare but possible) can be parsed as booleans in YAML 1.1. Quote tags defensively: image: "myapp:on". - Long env values without quotes. Values with
:, #, or { need quoting. POSTGRES_PASSWORD: hello:world fails — use "hello:world". - Anchor reuse for shared blocks. Compose supports YAML anchors —
x-default-env: &default-env at the top, then <<: *default-env inside services. Great for DRY, but anchors expand at parse time so the final shape (and any error messages) reference the expanded keys, not the alias.
This tool vs alternatives
| Tool | Best for | Trade-off |
|---|
| This validator | Quick browser check; no Docker install required | Heuristic only; not the full Compose Spec |
docker compose config | Authoritative; resolves env vars, anchors, profiles | Needs Docker installed and a working daemon |
compose-spec/compose-go | Programmatic validation in Go services | Requires Go toolchain and integration code |
yamllint | Generic YAML style checks (indent, line length) | Doesn't understand Compose semantics |
Common use cases
- CI pre-flight check — fail fast on a malformed compose file before spinning up runners.
- Copy-paste from docs — verify a snippet before pasting it into a production compose project.
- Helm rendered output — sanity-check the YAML produced by
helm template when comparing against compose-style examples. - Troubleshooting — narrow down which service definition is causing "invalid compose file" or version-mismatch errors.
- Onboarding / education — let new team members iterate on compose files without having Docker installed yet.
Related guides
JSON vs YAML: When to Use What — A Developer's Guide
JSON wins on APIs; YAML wins on configs. Side-by-side syntax, parser behaviour, and where each fits across Kubernetes manifests, REST payloads, and GitHub Actions.
Docker Compose for Beginners: From docker run to YAML
How to translate a pile of docker run commands into a single docker-compose.yml. Service definitions, named networks, volumes, env files, and the override patterns for dev vs prod.
YAML Configuration Files: Syntax, Best Practices, and Common Pitfalls
YAML whitespace is unforgiving and the Norway problem (no:) still bites. Types, anchors, aliases, and the rules that keep Kubernetes manifests, GitHub Actions, and Helm values readable.
Kubernetes Manifest Validation: Catch Errors Before kubectl Apply
The cluster is the most expensive place to discover a YAML typo. Schema validation, server-side dry-run, CRDs, and the apiVersion graveyard.
Frequently Asked Questions
Q: Does this replace docker compose config?
No — it catches roughly 90% of real-world bugs without needing Docker installed. docker compose config is still the authoritative check: it resolves env variables, expands anchors, applies profiles, and matches the exact Compose Specification version your CLI ships with. Use this validator in your editor for speed; use docker compose config in CI for completeness.
Q: My compose file uses extends: — does that work?
The validator parses the file as-is but does not resolve cross-file extends: references. Inline the extended definitions or run docker compose config to get the merged shape, then paste the rendered output here for the heuristic check.
Q: How does it handle env-var interpolation (${VAR})?
Interpolation is preserved as-is. image: ${REGISTRY}/web:${TAG} passes the structural check; whether REGISTRY and TAG are actually set in your shell is checked when docker compose up runs. Default values (${TAG:-latest}) are also preserved.
Q: Do profiles: work?
Yes — profiles are just metadata; the validator parses them but does not apply profile-filtering. Every service in the file is validated regardless of which profiles are active. For profile-filtered validation, run docker compose --profile dev config first.
Q: Is the compose file uploaded anywhere?
No. Parsing and validation run entirely in your browser. Secrets inlined as environment variables (POSTGRES_PASSWORD: s3cret) stay local — you can verify by opening DevTools and watching the Network tab while validating.
Q: Why is healthchecknot flagged when it's missing?
healthcheck: is optional in the Compose spec — many services run fine without it. This validator only flags required field absence. If your depends_on uses condition: service_healthy, double-check that the upstream service has a healthcheck: block, since otherwise it will never report healthy and the dependent service will wait indefinitely.
Limitations
This is a fast heuristic validator, not a full Compose spec implementation. It does not verify image registries, network reachability, or version-specific schema differences across Compose v2/v3/v3.x. For strict compliance with the Compose Specification, run docker compose config after the heuristic check passes.