Practice brief

Health, Logging & Debugging Exercises

The Compose file from Module 11 starts both services, but there is no signal to tell you when each one is actually ready. In Module 12 you add health checks so Compose can report service health and so depends_on can use a real readiness condition instead of just start order.

Build from scratch

12.1 Build from scratch
~25-35 min

Add Health Checks to the Compose Stack

The Compose stack from Module 11 has depends_on: db, but that only ensures the database container starts before the API — it does not guarantee Postgres is ready to accept connections. In this exercise you will add healthcheck definitions to both services and update depends_on to use condition: service_healthy. After this, docker compose ps reports actual service health, and the API only starts once Postgres passes its readiness check.

Try it yourself first. Open the guided path if you get blocked.

Step 1 — Add a health check to the db service

Open docker-compose.yml and add a healthcheck block to the db service. pg_isready is a Postgres utility that returns success when the server is accepting connections. The interval and timeout control how often Compose checks and how long to wait for a response. db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: secret POSTGRES_DB: taskflow volumes: - taskflow-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d taskflow"] interval: 5s timeout: 5s retries: 5 The test field uses CMD-SHELL so the command runs through the shell. retries: 5 means Compose tries five times before marking the service as unhealthy.

Step 2 — Update depends_on to require healthy status

Change the api service's depends_on from a simple list to the long form with a condition. api: build: . ports: - "8000:8000" environment: DATABASE_URL: postgres://postgres:secret@db:5432/taskflow depends_on: db: condition: service_healthy condition: service_healthy tells Compose to hold the api container in a waiting state until the db service passes its health check. This is what was missing in Module 11's break-fix exercise.

Step 3 — Add a health check to the api service

The API exposes a /health endpoint. Add a healthcheck that hits it. api: build: . ports: - "8000:8000" environment: DATABASE_URL: postgres://postgres:secret@db:5432/taskflow depends_on: db: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:8000/health || exit 1"] interval: 10s timeout: 5s retries: 3 wget is available in Alpine images. It fetches the URL silently (-q) and outputs the response (-O-). If the request fails the || exit 1 ensures the health check reports failure.

Step 4 — Restart the stack and observe health status

docker compose down --volumes
docker compose up -d
docker compose ps

docker compose down --volumes removes the old containers and the volume so Postgres initialises cleanly. After docker compose up -d, run docker compose ps every few seconds. The db service will show starting, then healthy. The api service will not start until db is healthy. Once both are healthy, the API is ready.

Step 5 — Verify the health endpoint responds

curl http://localhost:8000/health

A 200 response confirms the API is up and healthy. If you want to see the raw health check output from inside a container, run docker inspect --format='{{json .State.Health}}' taskflow-lab-api-1 and look at the Log field. Compose appends -1 to service names by default.

Break-fix

12.2 Break-fix
~15-25 min

The Health Check That Passes But the App Is Broken

Both services are showing as healthy in docker compose ps. The API responds to /health with 200. But when you hit /tasks, the response is 500. The health check is only testing that the server responds — it is not testing that the database connection is working. Reproduce this, find what /tasks returns in the logs, and fix the configuration that is causing the database error.

docker compose down --volumes
docker compose up -d
docker compose ps
curl http://localhost:8000/tasks
docker compose logs api
Show investigative hints
  • The health check passes because /health only confirms the HTTP server is responding. It does not check the database connection. The error is in the API logs — run docker compose logs api and read the error message.
  • The API logs will show a database connection error. Compare the DATABASE_URL in the Compose file to the POSTGRES_PASSWORD and POSTGRES_DB values on the db service.
  • A common cause of connection refused at this stage is a typo in DATABASE_URL — a wrong password, wrong database name, or wrong port. The error message from Postgres is specific about which part of the connection failed.
Show diagnosis and fix rationale

The API health endpoint only proves the HTTP server is up. The /tasks endpoint exercises the database path, so it reveals a DATABASE_URL mismatch. Compare the API connection string with the db service values and fix the wrong password, database name, host, or port. Once the connection string matches the Postgres service, /tasks should return 200.