Your App Started Before the Database Was Ready. Here's Why.
Paste your docker-compose.yml and see exactly which services are missing the healthcheck config that would have caught this. Takes five seconds, runs entirely in your browser.
How ComposeHealthCheck works
Paste your compose file
Copy the contents of your docker-compose.yml (or compose.yaml) and paste it into the input area. Any file with a top-level services key is accepted.
YAML is parsed locally
The file is parsed entirely in your browser with a bundled YAML parser. Each service is extracted with its image, depends_on entries, and healthcheck block.
The dependency graph is built
Every depends_on relationship becomes an edge in a graph: which service waits on which, and under what condition (service_started, service_healthy, or service_completed_successfully).
depends_on and healthcheck rules are checked
Each service is checked against known gotchas: a depends_on with no condition pointing at a service that has a healthcheck, a healthcheck test using curl or wget, missing timeout or retries, and start_period present (flagged as informational, since it's a grace period, not a delay).
The whole graph is checked for cycles
A depth-first search walks the entire depends_on graph looking for a service that, through some chain, ends up depending on itself. If found, the exact cycle is shown (A → B → C → A) instead of a generic error.
Results with a visual startup graph
Each service gets a severity rating (Critical, Warning, Info, or Clean) with specific findings and a plain-English fix, alongside a diagram showing the full startup order at a glance.
What the results mean
The service is part of a circular dependency. Compose cannot compute a valid startup order for services that wait on each other, directly or through a chain, and will error at startup.
A depends_on or healthcheck configuration issue that will likely cause a real startup-order or false-unhealthy bug, but doesn't stop Compose from starting.
Not a bug, a reminder about a commonly misunderstood field. Worth reading once, then safe to ignore if you already account for it.
No issues detected for this service. Its depends_on conditions match what its targets actually support, and its healthcheck (if any) is fully specified.
depends_on and healthcheck syntax reference
ComposeHealthCheck understands both the short and long forms of depends_on, and every field in a healthcheck block.
depends_on — short form (list)
services:
api:
depends_on:
- db
- redisdepends_on — long form (with condition)
services:
api:
depends_on:
db:
condition: service_healthy
migrator:
condition: service_completed_successfullyhealthcheck — full field reference
services:
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s # time between checks
timeout: 5s # how long one check may take
retries: 5 # consecutive failures before "unhealthy"
start_period: 10s # grace period; failures here don't count
disable: falsetest accepts a CMD array (exec form, no shell), a CMD-SHELL array (runs through /bin/sh -c), or a plain string (treated the same as CMD-SHELL). All three parse identically for the checks on this page.
When to use ComposeHealthCheck
| Scenario | What to paste |
|---|---|
| Right before running docker compose up on a new file for the first time | Your full docker-compose.yml before you ever run it |
| App keeps getting "connection refused" against the database on startup | The compose file, to check whether depends_on is actually waiting for the database's healthcheck |
| A healthcheck reports unhealthy but the app logs look fine | The service's healthcheck block, to check whether it calls a binary that might not be in the image |
| Compose fails at startup with a dependency-related error | The full file, to get the exact circular dependency chain instead of a generic error |
| Reviewing a teammate's or a tutorial's compose file before adopting it | Their shared docker-compose.yml, to sanity-check startup ordering before you copy it |
| Adding a new service with a dependency to an existing, working compose file | The updated file, to confirm the new depends_on entry doesn't introduce a cycle |
Frequently Asked Questions
What is ComposeHealthCheck and what does it check?
ComposeHealthCheck is a browser-based tool that reads your docker-compose.yml and checks the startup-order logic that the official schema validator doesn't cover. It flags depends_on entries that are missing condition: service_healthy on a target that actually has a healthcheck, healthcheck commands that call curl or wget without confirming those binaries exist in the image, missing timeout or retries fields, and circular dependency chains across the whole depends_on graph.
Everything runs entirely in your browser using a YAML parser bundled with the page. No data is sent to any server.
How is this different from docker compose config?
| ComposeHealthCheck | docker compose config | |
|---|---|---|
| Checks YAML syntax | Yes | Yes |
| Checks schema validity | Partial | Yes (authoritative) |
| Flags missing service_healthy | Yes | No |
| Detects circular dependencies with the actual cycle shown | Yes | Generic error only |
| Warns about curl/wget in minimal images | Yes | No |
| Requires Docker installed | No (browser only) | Yes |
docker compose config is the authoritative source for whether your YAML is structurally valid. ComposeHealthCheck is a complement, not a replacement: it looks for logical mistakes in depends_on and healthcheck configuration that a schema validator has no reason to flag, because they're all valid YAML, just not what you probably meant.
Is my compose file sent anywhere?
No. ComposeHealthCheck runs 100% client-side in your browser. The YAML you paste is parsed locally with JavaScript, and every check runs against that local, in-memory result. No network requests are made, and no data leaves your browser, so it's safe to paste a file with real service names, ports, and internal hostnames.
Why doesn't depends_on wait for my database to actually be ready?
By default, depends_on only waits for the target container to be in a running state, not for the process inside it to be ready to accept connections. Postgres, for example, reports as running well before it finishes initialization. To make a dependent service wait for actual readiness, add a healthcheck to the target and reference it with condition: service_healthy.
services:
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
api:
image: my-app
depends_on:
db:
condition: service_healthyWhat's the difference between service_started and service_healthy?
- <strong>service_started</strong> (the default) — waits only until the target container's process has started. Says nothing about whether the app inside is ready to do anything.
- <strong>service_healthy</strong> — waits until the target's healthcheck reports healthy. Requires the target to actually define a healthcheck block; if it doesn't, this condition can never be satisfied and Compose will error on startup.
- <strong>service_completed_successfully</strong> — waits until the target container runs to completion and exits with code 0. Used for one-off init or migration containers, not long-running services.
Why does my healthcheck fail with "executable file not found"?
This almost always means your healthcheck's test command calls a binary, commonly curl or wget, that isn't installed in the image. Minimal base images (alpine, slim, distroless) frequently ship without either. Docker runs the healthcheck test inside the container itself, so if the binary isn't there, the command fails immediately and the container is reported unhealthy, even if the application inside is working perfectly.
# Alpine images usually need curl installed explicitly
RUN apk add --no-cache curlAlternatively, use a binary you already know is present, such as a small script bundled with your app, or your language runtime's own HTTP client (node -e "..." or python3 -c "...") instead of adding a new package just for the healthcheck.
How do I fix a circular dependency error?
A circular dependency means two or more services depend on each other, directly or through a chain, so Compose has no valid order to start them in. ComposeHealthCheck shows you the exact chain (for example, worker → queue → worker) instead of Compose's generic error.
- <strong>Remove one direction of the dependency</strong> if only one service genuinely needs to wait for the other at startup.
- <strong>Introduce a third, lower-level service</strong> (like a shared queue or database) that both depend on instead of depending on each other directly.
- <strong>Handle the dependency in application code</strong> with a retry/backoff loop instead of a startup-order guarantee, if the two services are meant to be mutually communicating peers rather than strictly ordered.