157

docker-compose 2.1 offers the nice feature to specify a condition with depends_on. The current docker-compose documentation states:

Version 3 no longer supports the condition form of depends_on.

Unfortunately the documentation does not explain, why the condition form was removed and is lacking any specific recommondation on how to implement that behaviour using V3 upwards.

andolsi zied
  • 3,553
  • 2
  • 32
  • 43
m o
  • 1,807
  • 2
  • 11
  • 12

6 Answers6

74

There's been a move away from specifying container dependencies in compose. They're only valid at startup time and don't work when dependent containers are restarted at run time. Instead, each container should include mechanism to retry to reconnect to dependent services when the connection is dropped. Many libraries to connect to databases or REST API services have configurable built-in retries. I'd look into that. It is needed for production code anyway.

Bernard
  • 16,149
  • 12
  • 63
  • 66
71

From 1.27.0, 2.x and 3.x are merged with COMPOSE_SPEC schema.

version is now optional. So, you can just remove it and specify a condition as before:

services:
  web:
    build: .
    depends_on:
      redis:
        condition: service_healthy
  redis:
    image: redis
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 3s
      retries: 30
quanta
  • 3,960
  • 4
  • 40
  • 75
  • 5
    it does not work with docker-compose 1.29 and docker stack deploy – brunocascio Jul 22 '21 at 19:06
  • Removing the `version` field may drastically change the way IDE syntax highlighting treats the `depends_on` long syntax, from red bleed to just normal. – akauppi Mar 06 '22 at 10:19
  • If this answer doesn't work for you and you're using Linux, try checking the current docker-composer version: `which -a docker-compose; docker-compose -v` this will show the version and if you are currently using docker-compose installed/built locally (i.e. in your home dir) or the default /usr/bin one. You can install the latest docker-compose using pip or other forms of installation: https://docs.docker.com/compose/install/ – Savvas Radevic Apr 02 '22 at 22:49
  • 1
    And to add: `docker-compose` is anyways not state-of-the-art. Remove it. `docker compose` is where you want to go. If not installed, e.g. `apt-get install docker-compose-plugin` to enable it. Having considered all options, I think `healthcheck` plus `depends_on` with `service_healthy` condition seems best! Without `service_healthy` condition, the health check is entirely ignored. What also confused me: compose file format v2/v3 have NOTHING to do with docker-compose version 2 (there is no 3 at this moment). The mentioned merge is state-of-the-art with docker-compose currently being at 2 – IceFire Nov 27 '22 at 13:00
22

There are some external tools that let you mimic this behaviour. For example, with the dockerize tool you can wrap your CMD or ENTRYPOINT with dockerize -wait and that will prevent running your application until specified services are ready.

If your docker-compose file used to look like this:

version: '2.1'
services:
  kafka:
    image: spotify/kafka
    healthcheck:
      test: nc -z localhost 9092
  webapp:
     image: foo/bar # your image
     healthcheck:
       test: curl -f http://localhost:8080
  tests:
     image: bar/foo # your image
     command: YOUR_TEST_COMMAND
     depends_on:
       kafka:
         condition: service_healthy
       webapp:
         condition: service_healthy

then you can use dockerize in your v3 compose file like this:

version: '3.0'
services:
  kafka:
    image: spotify/kafka
  webapp:
     image: foo/bar # your image
  tests:
     image: bar/foo # your image
     command: dockerize -wait tcp://kafka:9092 -wait web://webapp:8080 YOUR_TEST_COMMAND
Jakub Kukul
  • 12,032
  • 3
  • 54
  • 53
9

Just thought I'd add my solution for when running postgres and an application via docker-compose where I need the application to wait for the init sql script to complete before starting.

dockerize seems to wait for the db port to be available (port 5432) which is the equivilant of depends_on which can be used in docker 3:

version: '3'

services:
  app:
    container_name: back-end
    depends_on:
      - postgres
  postgres:
    image: postgres:10-alpine
    container_name: postgres
    ports:
      - "5432:5432"
    volumes:
      - ./docker-init:/docker-entrypoint-initdb.d/

The Problem:

If you have a large init script the app will start before that completes as the depends_on only waits for the db port.

Although I do agree that the solution should be implemented in the application logic, the problem we have is only for when we want to run tests and prepopulate the database with test data so it made more sense to implement a solution outside the code as I tend not like introducing code "to make tests work"

The Solution:

Implement a healthcheck on the postgres container. For me that meant checking the command of pid 1 is postgres as it will be running a different command on pid 1 while the init db scripts are running

Write a script on the application side which will wait for postgres to become healthy. The script looks like this:

#!/bin/bash
function check {
  STATUS=\`curl -s --unix-socket /var/run/docker.sock http:/v1.24/containers/postgres/json | python -c 'import sys, json; print json.load('sys.stdin')["State"]["Health"]["Status"]'\`

  if [ "$STATUS" = "healthy" ]; then
    return 0
  fi
  return 1
}

until check; do
  echo "Waiting for postgres to be ready"
  sleep 5
done

echo "Postgres ready"

Then the docker-compose should mount the directories of the scripts so that we don't edit the Dockerfile for the application and if we're using a custom postgres image, this way we can continue to use the docker files for your published images.

We're also overriding the entry point defined in the docker file of the app so that we can run the wait script before the app starts

version: '3'

services:
  app:
    container_name: back-end
    entrypoint: ["/bin/sh","-c","/opt/app/wait/wait-for-postgres.sh && <YOUR_APP_START_SCRIPT>"]
    depends_on:
      - postgres
    volumes:
      - //var/run/docker.sock:/var/run/docker.sock
      - ./docker-scripts/wait-for-postgres:/opt/app/wait
  postgres:
    image: postgres:10-alpine
    container_name: postgres
    ports:
      - "5432:5432"
    volumes:
      - ./docker-init:/docker-entrypoint-initdb.d/
      - ./docker-scripts/postgres-healthcheck:/var/lib
    healthcheck:
      test: /var/lib/healthcheck.sh
      interval: 5s
      timeout: 5s
      retries: 10
Muji
  • 531
  • 6
  • 10
1

If one is looking for a docker stack version of this:

it looks like it is currently not possible to define the dependencies when using docker stack: https://github.com/docker/cli/issues/3880

tuxflo
  • 111
  • 3
  • The link provided is related to `docker swarm` not `docker compose` itself. – Raymond A Mar 16 '23 at 09:43
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 16 '23 at 09:43
  • 1
    Yes, that's why I mentioned it. It is worth noting, that this key will be ignored if you deploy the `docker-compose` file into a docker stack. Which is what people might do. – tuxflo Mar 17 '23 at 12:37
0

I reached this page because one container would not wait for the one depending upon and I had to run a docker system prune to get it working. There was an orphaned container error that prompted me to run the prune.

obotezat
  • 1,041
  • 16
  • 20