docker compose up --wait (Tip #51) waits for services to become healthy. docker compose wait does something different: it waits for services to exit, and returns their exit code.

Basic usage

docker compose wait <service>

The command blocks until the specified service stops, then prints the exit code. If you echo $? after, it’s the same value.

This is perfect for one-shot services: migrations, batch jobs, test runners, anything that runs and exits.

A migration example

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 5s

  migrate:
    image: myapp-migrate
    command: ["./run-migrations.sh"]
    depends_on:
      db:
        condition: service_healthy

  app:
    image: myapp
    depends_on:
      db:
        condition: service_healthy
      migrate:
        condition: service_completed_successfully

Run the migration explicitly and only proceed if it succeeds:

docker compose up -d migrate
docker compose wait migrate

if [ $? -ne 0 ]; then
  echo "Migration failed"
  docker compose down
  exit 1
fi

docker compose up -d app

wait vs up –wait

The two commands solve different problems:

GoalCommand
Wait for services to be ready to serve (healthy)docker compose up --wait
Wait for a service to finish its work (exit)docker compose wait

Use both together in a CI pipeline:

# Bring up the stack and wait for everything to be healthy
docker compose up -d --wait --wait-timeout 60

# Run the test suite
docker compose up -d tests

# Wait for tests to finish, propagate exit code
docker compose wait tests
TESTS_EXIT=$?

docker compose down --volumes
exit $TESTS_EXIT

Waiting for multiple services

You can wait on more than one service:

docker compose wait migrate seeder

The command returns once all named services have exited. If any of them fail, the exit code reflects the failure.

Pairing with –exit-code-from

docker compose up --exit-code-from <service> (Tip #52) is the all-in-one alternative: start everything, wait for the named service to exit, return its exit code. wait gives you the same result with more control — you can start the stack first, then wait at the right moment in your script.

For simple one-step pipelines, --exit-code-from is cleaner. For multi-step scripts where you orchestrate services individually, wait is more flexible.

Further reading