Lädt...


🔧 Wait for Services to Start in Docker Compose: wait-for-it vs Healthcheck


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

When developing applications using Docker Compose, it's common to encounter situations where one service must wait for another to be ready before starting. For example, a web application might need to wait for a database or another dependent service to be up and running. In this post, we'll explore two popular ways to solve this problem:

  1. Using the wait-for-it.sh script
  2. Leveraging depends_on and healthchecks in Docker Compose

We'll create a simple Python application for demonstration, but don't worry if you're not familiar with Python—you can just copy the code.

Setting up the Environment

Let's start by creating a working directory:

mkdir waitforit
cd waitforit

Next, we’ll build a simple aiohttp-based web application with a /healthz endpoint for health checks. We’ll also add a start delay using an environment variable SLEEP_BEFORE_START, which allows us to simulate service startup delays. The delay can be set by passing the number of seconds in this variable.

File: app.py

import os
import time
from aiohttp import web

async def healthz(request):
    return web.Response(text="OK")

if __name__ == "__main__":
    sleep_time = int(os.getenv("SLEEP_BEFORE_START", 0))
    print(f"Sleeping for {sleep_time} sec before starting...")
    time.sleep(sleep_time)

    app = web.Application()
    app.add_routes([web.get("/healthz", healthz)])
    print("Starting...")
    web.run_app(app, host="0.0.0.0", port=8080)

Now, let's create a Dockerfile for the application.

File: Dockerfile

FROM python:3.9-slim

WORKDIR /app
RUN pip install aiohttp \
    && apt-get update \
    && apt-get install -y curl
COPY app.py .
CMD ["python", "-u", "app.py"]

Next, we’ll create a docker-compose.yml file to launch two services:

  1. web – the web app with a startup delay.
  2. dependent – a service that needs to wait until web is fully up.

File: docker-compose.yml

version: "3.8"

services:
  web:
    image: sleep-web-app
    build: .
    environment:
      SLEEP_BEFORE_START: 10

  dependent:
    image: sleep-web-app
    depends_on:
      - web

Let's start the containers and observe the result:

docker compose up --build

You’ll notice that dependent starts before web is fully ready:

web-1        | Sleeping for 10 sec before starting...
dependent-1  | Sleeping for 0 sec before starting...
dependent-1  | Starting...
web-1        | Starting...

Our goal is to configure Docker Compose so that dependent only starts after web is fully ready. The desired log should look like this:

web-1        | Sleeping for 10 sec before starting...
web-1        | Starting...
dependent-1  | Sleeping for 0 sec before starting...
dependent-1  | Starting...

Using wait-for-it.sh

wait-for-it.sh is a simple Bash script that blocks service execution until another service becomes available, checking the availability of a specific port. It’s useful for waiting on databases, web services, or any other TCP services.

The script needs to be run before the main command to start the application. This can be done in the entrypoint or by directly inserting it before the start command. In our example, we'll use the second method for simplicity.

As arguments, it is enough to pass the host:port whose availability we expect. The script will then wait for 15 seconds (the default value). To set your own wait time, you can add the argument --timeout <sec>. If the port becomes available within that time, the script will stop, and the service will continue to start. However, if the host:port remains unavailable after the timeout, the service will still start. To avoid this and stop the launch with an error status, you must pass the --strict flag.

Here’s how to download and use it:

curl -o wait-for-it.sh https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh
chmod +x wait-for-it.sh

Let's add a dependent volume to the service, through which we will pass the script. We will also add the application startup command using wait-for-it.sh. The script will wait for the web service to be ready on port 8080. The main command to start the service is executed after --. You can experiment with the parameters and different startup scenarios. We will consider an example where we have a rough idea of how long to wait for the web service (10 seconds) and will leave the timeout at the default (15 seconds).

  dependent:
    image: sleep-web-app
    command:
      - ./wait-for-it.sh
      - web:8080
      - --
      - python
      - -u
      - app.py
    volumes:
      - ./wait-for-it.sh:/app/wait-for-it.sh

Run the services again:

docker-compose up

The log shows that dependent now waits for web to be available before starting:

dependent-1  | wait-for-it.sh: waiting 15 seconds for web:8080
web-1        | Sleeping for 10 sec before starting...
web-1        | Starting...
dependent-1  | wait-for-it.sh: web:8080 is available after 11 seconds
dependent-1  | Starting...

Pros of wait-for-it.sh:

  • Easy to implement: just download and run before the main command.
  • Flexible: you can wait for any port on any service, even external ones.
  • Simple: no additional configuration needed.

Cons:

  • It only checks port availability, not the full readiness of the service (e.g., database schema initialization). The next approach can solve this.

Using depends_on and healthcheck

Another option is to use Docker Compose’s depends_on in combination with healthcheck. This approach waits for a service to pass a health check before starting dependent services.

In this approach, the service must have a healthcheck that checks its readiness to handle requests, while the dependent service should have depends_on with a condition based on the healthcheck of the first service. In simple terms, the dependent service starts when the first service is healthy (ready to handle requests).

Let's add a healthcheck for the web service to check its readiness via the /healthz endpoint. This way, we will know that not only is the port open, but the service is also ready to process requests. You can read more about healthcheck here. In test, we specify the command that checks the service's health. The command could simply check if the port is open, which would be equivalent to wait-for-it.sh. However, the essence of healthcheck is not just to check the port, but to verify that the service is ready to handle requests.

  web:
    image: sleep-web-app
    build: .
    environment:
      SLEEP_BEFORE_START: 10
    healthcheck:
      test: ["CMD", "curl", "http://web:8080/healthz"]
      interval: 10s # How often the status will be checked
      retries: 5 # How many times to check before considering it unavailable
      start_period: 10s # How long after startup to begin checks
      timeout: 10s # Timeout for each test run

We will add depends_on with a condition to the dependent service. The dependent service relies on web, but it will only start after the web service successfully passes the health check. You can read more about conditional depends_on here. It's worth mentioning that not all versions of Compose support conditional depends_on.

Final Compose:

version: "3.8"

services:
  web:
    image: sleep-web-app
    build: .
    environment:
      SLEEP_BEFORE_START: 10
    healthcheck:
      test: ["CMD", "curl", "http://web:8080/healthz"]
      interval: 10s
      retries: 5
      start_period: 10s
      timeout: 10s

  dependent:
    image: sleep-web-app
    depends_on:
      web:
        condition: service_healthy

Run the services:

docker-compose up

This time, the dependent service will start execution only after the health check of the web service via /healthz is successful.

web-1        | Sleep 10 sec before start...
web-1        | Starting...
dependent-1  | Sleep 0 sec before start...
dependent-1  | Starting...

Popular services have ready-made solutions for healthcheck:

postgresql: ["CMD", "pg_isready", "-U", "postgres"]
kafka: ["CMD-SHELL", "kafka-broker-api-versions.sh --bootstrap-server localhost:9092"]
redis: ["CMD", "redis-cli", "ping"]
mysql: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
mongodb: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]
elasticsearch: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]

Pros of depends_on + healthcheck:

  • Health checks give a more reliable indication of service readiness compared to port checks.
  • It’s a built-in Docker Compose solution — no need for extra scripts.
  • Easier to manage chains of service dependencies.

Cons:

  • More configuration required.
  • Not all images come with health checks by default, and custom scripts may be needed.
  • It’s harder to check external services outside of Compose.

Which Method to Choose?

The choice between wait-for-it.sh and depends_on with healthcheck depends on your use case:

  • Use wait-for-it.sh for quick and simple port availability checks, especially in environments where you have more control over dependencies.

  • Use depends_on + healthcheck for a more reliable, built-in Docker Compose solution that ensures full service readiness.

Conclusion

Managing service readiness in Docker Compose is crucial for building reliable multi-service applications. Whether you opt for the simplicity of wait-for-it.sh or the more robust depends_on with health checks, both methods help ensure that services wait for dependencies to be fully ready before starting. By choosing the right strategy, you can avoid potential issues and make service startup smoother in your Docker-based applications.

...

🔧 Wait for Services to Start in Docker Compose: wait-for-it vs Healthcheck


📈 87.74 Punkte
🔧 Programmierung

🐧 Using Podman and Docker Compose - Podman 3.0 now supports Docker Compose


📈 38.58 Punkte
🐧 Linux Tipps

🕵️ Medium CVE-2020-7606: Docker-compose-remote-api project Docker-compose-remote-api


📈 38.58 Punkte
🕵️ Sicherheitslücken

🔧 Docker Advanced Concepts - Docker Compose and Docker Swarm: Day 6 of 50 days DevOps Tools Series


📈 34.06 Punkte
🔧 Programmierung

🐧 Docker Stack Tutorial | Docker Stack Deploy Docker-Compose.yml


📈 34.06 Punkte
🐧 Linux Tipps

🔧 Dynamically start Docker Compose Services with a simple Makefile


📈 30.18 Punkte
🔧 Programmierung

🐧 Creating Awesome Labs with Docker/Docker-Compose and Google Cloud Shell


📈 26.68 Punkte
🐧 Linux Tipps

🐧 How to Install Docker and Docker Compose on Raspberry Pi


📈 26.68 Punkte
🐧 Linux Tipps

🐧 Node.js development with Docker and Docker Compose


📈 26.68 Punkte
🐧 Linux Tipps

🔧 Docker - Utilize Docker Compose for Local Development Environments


📈 26.68 Punkte
🔧 Programmierung

🐧 How to deploy on remote Docker hosts with docker-compose


📈 26.68 Punkte
🐧 Linux Tipps

🐧 What is the Purpose of a docker-compose.yml File in Docker?


📈 26.68 Punkte
🐧 Linux Tipps

🔧 C# (C Sharp) CRUD Rest API using .NET 7, ASP.NET, Entity Framework, Postgres, Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 DOCKER and DOCKER COMPOSE: Run Dependencies Without Installing Them in Your Environment


📈 26.68 Punkte
🔧 Programmierung

🔧 Kotlin CRUD Rest Api using Spring Boot, Hibernate, Postgres, Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 SETTING UP A DOCKER BASED GITLAB-CE(COMMUNITY EDITION) AND A GITLAB RUNNING USING DOCKER COMPOSE


📈 26.68 Punkte
🔧 Programmierung

🔧 Docker ve Docker Compose Kurulumu (Ubuntu 20.04)


📈 26.68 Punkte
🔧 Programmierung

🔧 CouchDB: Offline-first with multi-master synchronization using Docker and Docker-compose


📈 26.68 Punkte
🔧 Programmierung

🔧 Python CRUD Rest API, using: Django, Postgres, Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 Python CRUD Rest API using Flask, SQLAlchemy, Postgres, Docker, Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 Python CRUD Rest API, using: Django, Postgres, Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 Best Practices of Docker & Docker-Compose for NextJS application.


📈 26.68 Punkte
🔧 Programmierung

🔧 TypeScript CRUD Rest API, using: Nest.js, TypeORM, Postgres, Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 Exploring Docker Further: Docker Compose and Multi-Container Apps


📈 26.68 Punkte
🔧 Programmierung

🔧 Java CRUD Rest API using Spring Boot, Hibernate, Postgres, Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 Docker Run vs Docker Compose: Which One Should You Use for Container Management?


📈 26.68 Punkte
🔧 Programmierung

🐧 Why is Docker Installed But Not Docker Compose?


📈 26.68 Punkte
🐧 Linux Tipps

🔧 Modern Web Development with Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🔧 Mastering Docker: Containers, Images, Networks, Optimization, and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🎥 Docker Tutorial Bonus - Docker Compose Dateiformat Version


📈 26.68 Punkte
🎥 IT Security Video

🔧 The Functional Depth of Docker and Docker Compose


📈 26.68 Punkte
🔧 Programmierung

🎥 Docker Tutorial #28 - Mehr Optionen in Docker Compose Files


📈 26.68 Punkte
🎥 IT Security Video

🔧 Simple Node Application with Docker + Nginx + Postgres + Docker Compose


📈 26.68 Punkte
🔧 Programmierung

📰 FAQ: Container mit Docker und die Feinheiten von Docker-Compose


📈 26.68 Punkte
📰 IT Nachrichten

matomo