Quick Links

Docker Compose lets you start multiple containers by running a single command. This simplifies bringing up complex services formed from several independent components.

This isn't always good enough though. Some of your containers might have dependencies on each other that break the application if they can't be fulfilled. In this guide, we'll show how you can configure your Compose services to accommodate these dependencies, making it possible to start containers in order.

The Basics

Docker Compose supports a

        depends_on
    

field in

        docker.compose.yml
    

files. Services can include the names of their siblings in

        depends_on
    

. This prevents the container from starting until the depended-on services are up.

services:
    

api:

image: example.com/api:latest

depends_on:

- db

web-app:

image: example.com/web-app:latest

depends_on:

- api

db:

image: mysql:8.0

In this example, the

        depends_on
    

fields cause the services to start in the following order:

  •         db
        
  •         api
        
  •         web-app
        

Each service's dependencies are resolved recursively. The service that defines each

        depends_on
    

field is started last, at the very end of the chain. When a service depends on multiple other containers, they'll be started in the order they're listed in the

        depends_on
    

field.

The chain of services is used in reverse when you stop a stack with

        docker-compose stop
    

. With the example above, the

        web-app
    

container will be deleted first, then

        api
    

and

        db
    

. This prevents requests to the

        web-app
    

container from failing as a tear-down operation commences.

Waiting for Readiness

The default

        depends_on
    

configuration only waits for dependent containers to start. In the example above, Compose can create the

        api
    

container as soon as

        db
    

is running, even if the database server inside the container isn't ready to receive connections. This means

        depends_on
    

is rarely sufficient on its own.

You can combine the feature with healthchecks to prevent containers from starting until their dependencies are actually ready. To use this capability, nest a

        condition
    

field under

        depends_on
    

with

        service_healthy
    

as its value:

services:
    

api:

image: example.com/api:latest

depends_on:

- db

healthcheck:

test: curl --fail http://127.0.0.1 || exit 1

interval: 10s

retries: 5

start_period: 5s

timeout: 10s

web-app:

image: example.com/web-app:latest

depends_on:

api:

condition: service_healthy

db:

image: mysql:8.0

Now the

        api
    

container has a healthcheck command attached. The

        web-app
    

service is instructed not to start until

        api
    

has been created with a successful healthcheck result. This will be once the API starts responding to requests and the

        curl
    

command exits with a zero status code.

Waiting for a Successful Container Exit

In some cases your dependency might be a single-use container that you want to run to completion. You can wait on this kind of dependency by setting the

        condition
    

field to

        service_completed_successfully
    

. This is useful when you've got a first-run setup script that executes in another container.

services:
    

app:

image: example.com/app:latest

depends_on:

config_builder:

condition: service_completed_successfully

volumes:

- config:/opt/app/config

config_builder:

image: example.com/config_builder:latest

env:

- EXAMPLE_KEY

- ANOTHER_KEY

volumes:

- config:/output

volumes:

config:

This example shows how a dependent image could run a command that writes a config file to a volume shared by

        app
    

. After the data's been written, the

        config_builder
    

container stops with a zero exit code. Compose then automatically starts the

        app
    

service as its dependency condition has been met.

More Control With Other Tools

In some situations

        depends_on
    

with a

        condition
    

might still not be enough to accommodate your use case. You can add external tools to manually implement health checks and handle links between containers.

Wait-for-It is a utility script which wraps another process. It'll run the command you specify after a certain condition is met. This can be used to define healthcheck procedures independently of Docker's built-in support.

Here's how to use

        healthcheck
    

to wait for a linked container port to become accessible:

services:
    

api:

image: example.com/api:latest

depends_on:

- db

web-app:

image: example.com/web-app:latest

depends_on:

- api

command: ["./wait-for-it.sh", "api:8080", "--", "node", "app.js"]

db:

image: mysql:8.0

Here we've reverted to only making Docker Compose wait for the

        api
    

container to start. The

        web-app
    

service accepts responsibility for checking whether

        api
    

is healthy. It uses the Wait-for-It script to detect when the container is accessible on port 8080. Wait-for-It will then launch the web app container's real command, defined as

        node app.js
    

.

This approach is best reserved for specific situations where you can't set up a proper healthcheck with Docker. It might be necessary when you're using a third-party image that can't be configured to run a healthcheck command. Wait-for-It provides a way of detecting whether a port's serving traffic as a stand-in replacement. While not infallible, this is often a good indicator of a container's healthiness.

Summary

Docker Compose defaults to simultaneously starting all the services in your stack. This is often undesirable when links between the services create parent-child dependency relationships.

The

        depends_on
    

field lets you define a startup sequence for your services. Compose will create each new container in order, guaranteeing the previous one has started before the next container is added.

You can wait for the previous container to exit or report a positive healthcheck by adding a

        condition
    

to the dependency definition. In situations where healthchecks can't be used, you can defer to tools like Wait-for-It to have parent containers detect when their dependencies are ready.