Terminus
Understand depends_on in Docker Compose

Understand depends_on in Docker Compose

The short answer

In Docker Compose, [.inline-code]depends_on[.inline-code] field is used to express the dependencies between the services of an application and specify the order in which these services should be started and stopped.

In this example, the [.inline-code]api[.inline-code] service depends on the [.inline-code]database[.inline-code] service. When using the [.inline-code]docker compose up[.inline-code] command, Compose will start the [.inline-code]database[.inline-code] service first, then start the [.inline-code]api[.inline-code] service:

version: '3'

services:
  api:
    build: .
    depends_on:
      - database
  database:
    image: postgres

You can learn more about building applications running PostgreSQL with our article on how to launch PostgreSQL with Docker Compose.

[#easily-recall-syntax-with-ai]Easily retrieve this syntax using Warp AI feature[#easily-recall-syntax-with-ai]

If you’re using Warp as your terminal, you can easily retrieve this syntax using the Warp AI feature:

Entering [.inline-code]docker compose depends_on[.inline-code] in the AI question input will prompt a human-readable step by step guide including code snippets.

[#control-the-startup-with-conditions]Controlling the services startup with conditions[#control-the-startup-with-conditions]

By default, Compose only ensures that the services listed under the [.inline-code]depends_on[.inline-code] property are started before the dependent service. This means that Compose doesn't guarantee that these services are fully operational and ready to process requests, but only that their containers are running.

It is however possible to set conditions to define when one service should start or wait for another service to reach a specific state. These conditions are defined in the [.inline-code]condition[.inline-code] property and take three possible values: [.inline-code]service_started[.inline-code], [.inline-code]service_healthy[.inline-code], and [.inline-code]service_completed_successfully[.inline-code].

[#the-service-started-condition]The [.inline-code]service_started[.inline-code] condition [#the-service-started-condition]

This condition is the default condition used by Compose and only checks whether the service's container has started, without verifying the service's internal state or health.

In this example, Compose will first start the [.inline-code]database[.inline-code] service, then start the [.inline-code]api[.inline-code] service once the [.inline-code]database[.inline-code] service's container has started.

version: '3'

services:
  api:
    build: .
    depends_on:
      database:
        condition: service_started
  database:
    image: postgres

[#the-service-healthy-condition]The [.inline-code]service_healthy[.inline-code] condition [#the-service-healthy-condition]

This condition is used to ensure not only that the service's container has started but also that the service inside the container has successfully passed a health check. It ensures that the service is fully operational and ready to accept requests.

It is often combined with the [.inline-code]healthcheck[.inline-code] property that allows developers to define a custom health check for a given service. You can learn more about this property by reading the official Compose documentation page.

In this example, Compose will first start the [.inline-code]database[.inline-code] service, execute the [.inline-code]pg_isready[.inline-code] command specified in the [.inline-code]healthcheck.test[.inline-code] property, and then start the [.inline-code]api[.inline-code] service if the value returned by the test command is [.inline-code]0[.inline-code].

version: '3'

services:
  api:
    build: .
    depends_on:
      database:
        condition: service_healthy
  database:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]

[#the-service-completed-successfully-condition] The [.inline-code]service_completed_successfully[.inline-code] condition [#the-service-completed-successfully-condition]

This condition is used to ensure that the service's container not only starts and runs, but also completes its execution successfully. It's useful in scenarios where the dependent service is a one-time job or task, such as a data migration script, and you want to make sure it finishes without errors before starting another service.

To use [.inline-code]service_completed_successfully[.inline-code], you should ensure that the dependent service exits with a zero status code upon successful completion.

In this example, Compose will first start the [.inline-code]database[.inline-code] service, execute the [.inline-code]pg_isready[.inline-code] command specified in the [.inline-code]healthcheck.test[.inline-code] property, start the [.inline-code]migration[.inline-code] service if the value returned by the test command is [.inline-code]0[.inline-code], and finally start the [.inline-code]api[.inline-code] service if the exit status of the [.inline-code]migration[.inline-code] service's container is also [.inline-code]0[.inline-code].

version: '3'

services:
  api:
    build: .
    depends_on:
      database:
        condition: service_healthy
      migration:
        condition: service_completed_successfully
  database:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
  migration:
    image: migration-tool

[#restart-services-manually]Restarting services manually [#restart-services-manually]

When set to [.inline-code]true[.inline-code], the [.inline-code]restart[.inline-code] property allows you to automatically restart the dependent service whenever you perform a manual restart or operation using the [.inline-code]docker compose[.inline-code] command.

It's important to note that this setting doesn't affect the automated restart policy defined in the  service itself that is typically managed by the container runtime when the container exits unexpectedly.

In this example, Compose will restart the [.inline-code]api[.inline-code] service if the [.inline-code]database[.inline-code] service is manually restarted using the [.inline-code]docker compose restart[.inline-code] command. But it won't be restarted if the [.inline-code]database[.inline-code] service crashes and is restarted due to the [.inline-code]always[.inline-code] restart policy.

version: '3'

services:
  api:
    build: .
    depends_on:
      database:
        restart: true
  database:
    image: postgres
    restart: always

You can learn more about the restart command by reading the official Docker documentation page.

[#depend-on-external-services]Depending on external services [#depend-on-external-services]

The [.inline-code]depends_on[.inline-code] property only works for services defined within the same [.inline-code]compose.yaml[.inline-code] file. If your application relies on external services, like a database hosted on a remote server, you'll need to implement custom retry logic in your application to handle dependencies gracefully.

[#use-wait-for-scripts] Using wait-for scripts [#use-wait-for-scripts]

One way to achieve this is to use a "wait-for" script, that will periodically check if the external service is available before proceeding with your application's startup.

For example, the following script will check every 3 seconds if the status code of the HTTP response sent by the service's [.inline-code]/health[.inline-code] endpoint located at [.inline-code]127.0.0.1:8080[.inline-code] is [.inline-code]200[.inline-code]. It will then exit with a value of [.inline-code]0[.inline-code] if successfully connected to the service or with a value of [.inline-code]1[.inline-code] after 5 unsuccessful attempts.

#!/bin/bash

HOST="127.0.0.1"
PORT="8080"
MAX_ATTEMPTS=5
DELAY_BETWEEN_ATTEMPS=3

attempt=1

while true; do
  test=$(curl -s -I "$HOST:$PORT/health" | head -n 1 | cut -b 10-12)
  if [ "$test" == 200 ]; then
    exit 0
  fi

  sleep "$DELAY_BETWEEN_ATTEMPS"

  attempt=$((attempt + 1))
  if [ "$attempt" -gt "$MAX_ATTEMPTS" ]; then
    exit 1
  fi
done

This script can then be packaged into a standalone image running on a lightweight distribution such as Alpine Linux, and executed using the [.inline-code]service_completed_successfully[.inline-code] condition as demonstrated in the following example:

version: '3'

services:
  website:
    build: .
    depends_on:
      api:
        condition: service_completed_successfully
  api:
    image: wait-for-api

[#depends-on-vs-links]The [.inline-code]depends_on[.inline-code] vs [.inline-code]link[.inline-code] properties [#depends-on-vs-links]

While the [.inline-code]depends_on[.inline-code] property is used to express the dependencies between the services of an application, the [.inline-code]links[.inline-code] property is used to establish network communication between containers, which can be particularly useful for microservices architectures.

It allows one container to access services provided by another container using a specific alias, but doesn't whatsoever guarantee the availability or readiness of these services.