service "migrate" didn't complete successfully: exit 1 — another service waits on migrate with condition: service_completed_successfully, and migrate ended with exit code 1. The problem is inside the job, not in Compose: read its log, fix what made it fail, and run up again.

docker compose logs migrate shows what the job printed before it exited. Once the job exits with code 0, the services that wait on it start.

The error

PS> docker compose -p fix10-cs-p4 up
...
Attaching to migrate-1, web-1
 Container fix10-cs-p4-migrate-1 Starting
migrate-1  | applying migrations
migrate-1  | migration 002 failed
 Container fix10-cs-p4-migrate-1 Started
 Container fix10-cs-p4-migrate-1 Waiting
 Container fix10-cs-p4-migrate-1 Error service "migrate" didn't complete successfully: exit 1
 Container fix10-cs-p4-web-1 not started: condition "service_completed_successfully" of service "migrate" not met
service "migrate" didn't complete successfully: exit 1

The command exited with code 1. The file:

services:
  migrate:
    image: alpine
    command: ["sh", "-c", "echo applying migrations; echo migration 002 failed; exit 1"]
  web:
    image: alpine
    command: ["echo", "web started"]
    depends_on:
      migrate:
        condition: service_completed_successfully

Why it happens

service_completed_successfully tells Compose to start web only after migrate has run to the end with exit code 0. That suits one-off jobs such as database migrations or seed scripts. Here the job printed migration 002 failed and exited with code 1, so the condition was not met: Compose left web in the Created state, never started it, and returned the job's exit code in the message.

PS> docker compose -p fix10-cs-p4 ps -a
NAME                    IMAGE     COMMAND                  SERVICE   CREATED        STATUS                              PORTS
...
fix10-cs-p4-web-1       alpine    "echo 'web started'"     web       1 second ago   Created

The number after exit is the job's own exit code, so the place to look is the job's output, not the Compose file:

PS> docker compose -p fix10-cs-p4 logs migrate
migrate-1  | applying migrations
migrate-1  | migration 002 failed

The fix

Fix whatever the log reports, so the job ends with exit code 0. In this test the job's command was changed to succeed:

  migrate:
    image: alpine
    command: ["sh", "-c", "echo applying migrations; echo migration 002 applied; exit 0"]
PS> docker compose -p fix10-cs-p4 up
...
migrate-1  | applying migrations
migrate-1  | migration 002 applied
 Container fix10-cs-p4-migrate-1 Started
 Container fix10-cs-p4-migrate-1 Waiting
 Container fix10-cs-p4-migrate-1 Exited
 Container fix10-cs-p4-web-1 Starting
...
web-1      | web started
 Container fix10-cs-p4-web-1 Started
...

Once migrate reached Exited with code 0, Compose started web. Do not remove the condition to make the error go away: it is what kept the application from starting after a failed migration. Between the two runs the project was removed with docker compose down -v, so the second up created fresh containers and its log holds only the successful run.

How it was reproduced

Two alpine services, a migrate job whose shell command printed two lines and ran exit 1, and a web service with depends_on: migrate: condition: service_completed_successfully, started with docker compose up from Windows PowerShell 5.1, then the same file with exit 0. Docker Compose v5.7.0, Docker Engine 29.9.3, Docker Desktop 4.99.0 on Windows (version 10.0.26300). The project name fix10-cs-p4 is this test's own; it was removed with docker compose down -v after each run.

Frequently asked

What does 'service didn't complete successfully: exit 1' mean in docker compose?
A service that another service waits on with condition: service_completed_successfully ended with exit code 1. Compose did not start the waiting service. The number is the job's own exit code.
How do I find out why the Compose job failed?
Run docker compose logs followed by the service name, for example docker compose logs migrate. It shows what the job printed before it exited.
What does service_completed_successfully do in depends_on?
It makes Compose start the dependent service only after the named service has run to the end with exit code 0. In this test a job that exited with 0 let web start; one that exited with 1 left web in the Created state.

More decoded errors in the Fixes category. When the condition is service_healthy instead, see Docker Compose: dependency failed to start, container is unhealthy.