Skip to main content
Docker

How depends_on Works in Docker Compose

Learn how to use depends_on with health checks in Docker Compose. This tutorial demonstrates how to enhance service dependencies by integrating health checks, ensuring that dependent services are not just started but are also in a healthy state before others start.

Christian Schou

Are you struggling to make some services start or stop in the right order? Well, then you have come to the right place! 💪

In this short Docker Compose tutorial, I will show you how depends_on can help you in your compose files to specify the order of how your services are started and stopped when deploying a new Docker stack. 🐳

Services top-level element
Explore all the attributes the services top-level element can have.

Imagine you have an API service that needs a database service to be ready before it can start. When we deploy services using infrastructure as code (IaC), we are not in control with the mouse for coordinating the startup of our services. This issue can easily be solved using depends_on in the Docker Compose file describing your services.

Below is a classic Docker Compose example where depends_on is used. I will continue my explanation below on how you can control services during startup.

version: '3'

services:
  my_custom_app:
    build: .
    depends_on:
      - custom_db_server

  custom_db_server:
    image: mongo
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
      MONGO_INITDB_DATABASE: my_database

If you would like to know how you can launch a MongoDB server with Docker, you can check out the article below.

How To Control Services During Startup

Okay... before you implement depends_on in your Docker Compose file, please note the following.

🤓
Compose only makes sure that services added with depends_on are started before the depending service. Compose cannot guarantee that the database/service is ready to accept any incoming requests from other services.

So Christian... How can I get around that annoying problem? Ohh I am glad you asked! Conditions are the answer - with conditions we can define a certain property to be satisfied before the depending services will start. Currently, we have the following options.

  • service_started
  • service_healthy
  • service_completed_successfully

Let's take a look at these three options, step-by-step.

Using service_started in Docker Compose

When you use depends_on this is the condition Compose is looking for during startup. As I mentioned before, it does not verify if the database or specific service is in a ready state and that can lead to issues.

Before we fix this, (I will show you the fix later in this tutorial) I would like to show you how to set a condition on the app for the database using service_started. This will make sure that the database has started before launching the next service.

version: '3'

services:
  my_custom_app:
    build: .
    depends_on:
      custom_db_server:
        condition: service_started

  custom_db_server:
    image: mongo
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
      MONGO_INITDB_DATABASE: my_database

As you might have noticed we are now having a condition for the depends_on where we would like the service to be started before the my_custom_app is started. But that is not good, as we don't know if MongoDB is ready to accept connections.

Let's fix that issue by using health checks.

Using service_healthy in Docker Compose

Compose also has a condition named service_healthy, and that one is the perfect choice for making sure that our database/service is healthy and ready to work.

We can use this condition when a health check has been configured on the database/service we are depending on. This could be a ping, migrations, jobs, etc... We wan't to make sure this health check is successfully completed before staring other services depending on the failed service.

Let's see how that could be accomplished with a real-life example for MongoDB and our custom web app.

version: '3'

services:
  my_custom_app:
    build: .
    depends_on:
      custom_db_server:
        condition: service_healthy

  custom_db_server:
    image: mongo
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
      MONGO_INITDB_DATABASE: my_database
    healthcheck:
      test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]
      interval: 5s
      timeout: 3s
      retries: 5

In my example above, MongoDB/cutom_db_server will start up the database service, then run a command to test if the database is ready. When it report itself with a healthy state we will start up the other services depending on it.

I have configured the health check to check every 5 seconds, allow the service to be up to 3 seconds to respond and only report an unhealthy state when it has tried 5 times.

On the my_custom_app we have a condition on the depends_on for the database service and that is to make sure that the healthcheck is reporting healthy before starting up the app service.

Here are a few other health checks you can use on MongoDB if you need that.

Check the database connection

healthcheck:
  test:
    - "CMD"
    - "mongo"
    - "--quiet"
    - "127.0.0.1/test"
    - "--eval"
    - "'quit(db.runCommand({ ping: 1 }).ok ? 0 : 2)'"
  interval: 10s
  timeout: 10s
  retries: 5
  start_period: 40s

start_period is used to specify the time to wait before starting to run health checks (40 seconds in this case). This allows the service some time to start before health checks commence.

Ref: https://github.com/rodrigobdz/docker-compose-healthchecks#mongo

Check for a specific collection

healthcheck:
  test: ["CMD", "mongo", "--eval", "db.getCollectionNames()"]
  interval: 5s
  timeout: 3s
  retries: 5

Using service_completed_successfully in Docker Compose

We have a third option named service_completed_successfully and that one is very usefull if we want something to start, run a job like migrating a database, etc...

If you want this condition to be satisfied you need to make sure that the service running this one-time-job exits/stops with a status code 0 as it's the criteria of success.

Here is an updated version of the compose file with a migration service and the my_custom_app updated to have one more depends_on condition.

version: '3'

services:
  my_custom_app:
    build: .
    depends_on:
      my_custom_db:
        condition: service_healthy
      my_custom_migration:
        condition: service_completed_successfully

  my_custom_db:
    image: mongo
    healthcheck:
      test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
      MONGO_INITDB_DATABASE: my_database
      
  my_custom_migration:
    image: awesome-migration-job

This ensures that the my_custom_db is started and that the migrations has been applied to the database before we launch our application.

😅
From my time as a Software Engineer, I would recommend having the app running the migrations on the database, but this is just an example to show you how that specific condition works.

Summary

I hope you learned something from this short tutorial on how to use depends_on with Docker Compose.

In this tutorial, you have learned how to await dependent services based on different conditions and seen how you can utilize health checks to your advantage when starting services.

If you got any questions, please let me know in the comments below. Until next time happy Dockerizing stuff! ✌️