# Add Health Checks to a Docker Compose Setup

Yes. An agent reads each image for a probe tool, adds a healthcheck and a service_healthy dependency per service, proves it with compose up and opens a PR.

[Run this on Rush](https://cloud.getrush.ai/run/add-health-checks/docker-compose/), in a Project that has your repository.

## What the agent does

The agent clones the repository into its own cloud sandbox and reads every Compose file before it writes a `healthcheck`. In a collection like docker/awesome-compose there are 37 `compose.yaml` files and only 8 of them declare a `healthcheck`: the MySQL, MariaDB, Postgres and SQL Server samples, which pair it with `depends_on: db: condition: service_healthy`, and the Elasticsearch stack. The other 29 use the short `depends_on` list, which waits for a container to start, not for the service inside it to answer. `nginx-golang-postgres/compose.yaml` is the pattern the repository already likes: `test: ["CMD", "pg_isready"]`, `interval: 10s`, `timeout: 5s`, `retries: 5`, and a backend that depends on `service_healthy`.

A health check can only call what is inside the image, so the agent reads each Dockerfile for the stage Compose actually runs. In `nginx-golang`, `compose.yaml` builds the backend with `target: builder`, which is `golang:1.18-alpine`, so BusyBox `wget` is there and `wget -qO- http://localhost:80/` works against the chi handler in `main.go`; the final `FROM scratch` stage has no shell at all, and the agent says so rather than writing a check that would fail if someone switched the target. In `react-express-mongodb`, `mongo:4.2.0` answers `mongo --eval "db.adminCommand('ping')"`, and the backend runs on `node:lts-buster-slim` with no curl, so the check is `node -e` with an `http.get` against the router's `GET /` on port 3000. In `flask-redis`, `redis-cli ping` covers Redis and the `python:3.10-alpine` web container is probed with a one-line `urllib.request.urlopen` against port 8000.

For each service it writes a `healthcheck` with `interval`, `timeout`, `retries` and a `start_period` sized to how the image boots, then rewrites the short `depends_on` entries into the long form with `condition: service_healthy` where the dependency is a database or a cache, so `proxy` in `nginx-golang` waits for the backend to answer rather than for its container to exist. Services the repository marks as sharing a `restart: always` or `on-failure` policy keep it. The agent leaves `ports`, `volumes`, `secrets` and `networks` exactly as they are, since each sample's `README.md` quotes its `compose.yaml` and the diff should stay readable beside it.

Then it proves each change in the sandbox: `docker compose up --wait` in the sample's directory, `docker compose ps` to read every service as `healthy`, and `docker compose down -v` afterwards. A check that never turns healthy, such as a probe against a path the app does not serve, is fixed before the pull request; a service with no tool to probe and no reasonable sidecar is listed in the description as left alone, with the reason. The repository's `README.md` says the samples are for local development only, and the pull request keeps to that: the checks make `up` wait correctly, they are not a production monitoring design.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Add health checks to the Docker Compose samples in this repository. For every compose.yaml without a healthcheck, read each service's image and the Dockerfile stage Compose builds (the target in compose.yaml), and write a healthcheck that uses a tool present in that image: pg_isready, mysqladmin ping, redis-cli ping, mongo --eval ping, wget or curl where the image has it, or node -e or python -c with an HTTP request where it does not. Set interval, timeout, retries and a start_period, and rewrite short depends_on lists to the long form with condition: service_healthy for database and cache dependencies. Follow the style of nginx-golang-postgres/compose.yaml. Leave ports, volumes, secrets and networks unchanged. For each changed sample run docker compose up --wait, confirm every service is healthy in docker compose ps, then docker compose down -v. List any service you could not probe and why. Open a pull request with one commit per sample and a table of services and their checks.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request with one commit per sample, each changing only that sample's `compose.yaml`: new `healthcheck` blocks and `depends_on` rewritten to the long form. The description is a table with the sample, the service, the probe command and the image it relies on, followed by the short list of services left without a check and the reason, such as a `scratch` image with no shell.

A reviewer reads the table against the Dockerfiles once, looking for a probe that would break if the build target changed, and checks that no sample's `README.md` now quotes a stale `compose.yaml`. Because the repository asks for signed-off commits in `CONTRIBUTING.md`, the reviewer also checks that each commit carries a `Signed-off-by` line before merging.

The transcript keeps every `docker compose up --wait` and `docker compose ps` with its output per sample, so a service that is reported healthy in the pull request can be traced to the run that showed it. A follow-up on the same session can tune a `start_period` that turns out too short on slower machines.

## Run it on every Linear issue labelled agent

In the console, add an automation to the Project with this prompt and a `linear` trigger on `linear.issue.labeled`, filtered to the `agent` label. When someone labels an issue such as "backend starts before Postgres is ready in the Go sample", the trigger puts the issue after the prompt, the run scopes itself to the samples the issue names, and the pull request links back to it. Only label changes made by a person in your Rush org fire the trigger.

From a script, the same body goes to `POST /o/{org}/sessions` with the repository in `resources`; a `request_id` made from the issue id makes a retry safe. A key's `webhook_url` reports when a run that key started finishes, and the handler reads the pull request from `data.session_url`.

## Why run it on Rush?

Health checks are easy to write and hard to trust: the probe has to exist in the image, the path has to be one the app serves, and the only proof is bringing the stack up and watching `docker compose ps`. The agent does that for every changed sample in its own sandbox, with Docker available and nothing else running, and keeps the output, so the pull request's table is a record rather than a promise.

Doing it across a repository of samples by hand means reading dozens of Dockerfiles for the stage that actually runs. The agent reads them all, applies the one pattern the repository already uses, and leaves a per-sample commit the maintainers can merge or drop one at a time.

## Frequently asked questions

### What about images with no shell, like the scratch stage?

A `healthcheck` runs inside the container, so a `FROM scratch` image with a single static binary has nothing to run the probe with. The agent reports those services as left alone and names the alternative: a build target with a shell, as `nginx-golang` already uses, or a small health endpoint built into the binary. It does not add a sidecar container to a sample.

### Will the samples' READMEs go out of date?

Many READMEs in this repository quote their `compose.yaml` in full. The agent keeps the diff to the `healthcheck` and `depends_on` keys so the quoted blocks still match in substance, and the pull request lists the READMEs whose quoted file changed so a maintainer can decide whether to update the quotes in the same pull request or a later one.

### Does it add healthchecks to the proxy services too?

Only where the proxy image has a probe tool and a path to probe; the stock `nginx` image answers `GET /` once the backend is up, so a `curl` or `wget` check against it is written where the image provides one. Rewriting `proxy`'s `depends_on` to wait for a healthy backend is the part that fixes most startup races, and that is always done.

### Can it do the same in my app's single compose file?

Yes. The prompt is written for a repository of samples, but a single `compose.yaml` with a database, a cache and an app is the same job: read each image, write the probe it can run, make the app wait on `service_healthy`, and prove it with `docker compose up --wait` in the sandbox.
