Changer le thème

Dépendances Docker Compose : healthcheck pour l'ordre de démarrage des bases de données

Easton editorial illustration: environment switchboard

Vendredi soir, dix heures. Les logs d’erreur défilent dans le terminal depuis la dixième fois.

Le conteneur applicatif redémarre en boucle. La base de données est bien démarrée, mais toujours avec un décalage. Vous vérifiez docker-compose.yml — depends_on est configuré, pourquoi ça ne marche pas ?

Ce problème a frustré beaucoup de développeurs. En local, docker-compose up échoue souvent les deux premières fois ; il faut attendre une quinzaine de secondes et quelques redémarrages pour que tout tourne.

La cause est simple : depends_on ne gère que l’ordre de démarrage des conteneurs, pas si le service est réellement prêt.

Dans cet article, je vous montre pas à pas :

  • Les trois conditions de depends_on (90 % des gens ne connaissent que le défaut)
  • Le healthcheck correct pour PostgreSQL et MySQL (avec configuration complète)
  • L’alternative moderne à wait-for-it
  • Une checklist de dépannage pour un démarrage fiable

Pourquoi depends_on ne suffit pas : démarré ≠ prêt

La documentation Docker contient une phrase importante, souvent ignorée :

Compose does not wait until a container is “ready”, only until it’s running.

Traduction : Compose attend que le conteneur tourne, pas qu’il soit utilisable.

Décalage entre démarrage du conteneur et service prêt

Imaginez le démarrage d’un conteneur PostgreSQL :

  1. 0 s : Docker lance le conteneur, processus postgres démarre ← depends_on libère ici
  2. 2 s : initialisation du répertoire de données
  3. 5 s : chargement de la configuration
  4. 8 s : exécution des scripts init (le cas échéant)
  5. 12 s : enfin prêt, accepte les connexions

Entre les deux, 12 secondes de décalage. Si votre application web tente de se connecter à la 1re seconde, le résultat est « Connection refused ».

Un cas réel encore plus marqué : sur un projet legacy, le script d’initialisation importait 500 Mo de données de test — 40 secondes rien que pour ça. Avec le depends_on par défaut, le conteneur applicatif crashait et redémarrait au moins 5 fois avant de se connecter.

Les trois conditions de depends_on

Beaucoup ignorent que depends_on supporte trois conditions :

services:
  web:
    depends_on:
      db:
        condition: service_started  # défaut, conteneur démarré suffit
        # condition: service_healthy  # attend le passage du healthcheck
        # condition: service_completed_successfully  # attend la sortie réussie (init containers)

service_started (défaut) : le conteneur doit être en état running. C’est pourquoi depends_on ne résout pas toujours le problème.

service_healthy : le healthcheck doit passer, le conteneur passe à « healthy ». C’est ce dont nous avons besoin.

service_completed_successfully : attend une sortie réussie (exit code 0). Adapté aux migrations de données et tâches ponctuelles.

Pourquoi service_healthy n’est-il pas le défaut ?

Deux raisons :

  1. Tous les services n’ont pas besoin de healthcheck (ex. worker stateless)
  2. Le healthcheck doit être configuré par vous — Docker ne sait pas comment définir « prêt » pour votre service

D’où le sujet suivant : comment configurer le healthcheck.

Guide complet de configuration healthcheck

Le principe est simple : Docker exécute périodiquement une commande ; retour 0 = sain, retour 1 = non sain.

Configuration complète du healthcheck

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]  # commande de vérification
      interval: 10s       # vérification toutes les 10 s
      timeout: 5s         # timeout par vérification : 5 s
      retries: 3          # 3 échecs consécutifs avant unhealthy
      start_period: 30s   # échecs ignorés pendant les 30 premières secondes

Ces cinq paramètres sont importants — passons-les en revue.

test : commande de vérification

Deux formats :

# Méthode 1 : shell (recommandé)
test: ["CMD-SHELL", "pg_isready -U postgres"]

# Méthode 2 : exécution directe (sans shell)
test: ["CMD", "pg_isready", "-U", "postgres"]

En général, CMD-SHELL suffit — pipes et redirections shell disponibles.

Piège courant : l’outil de la commande doit exister dans l’image. Si vous utilisez curl pour un endpoint HTTP mais que l’image n’a pas curl, le healthcheck échoue toujours. J’ai perdu une demi-heure avant de réaliser qu’il fallait ajouter RUN apk add curl au Dockerfile.

interval : intervalle de vérification

Fréquence des vérifications. Trop fréquent = gaspillage ; trop lent = réaction tardive.

  • 10 s : bonne valeur par défaut pour la plupart des cas
  • Services critiques (base de données) : 5 s
  • Services légers : 15-30 s

timeout : timeout par vérification

Temps d’attente si la commande bloque.

Trop court = faux positifs ; trop long = détection lente. 5-10 s est une plage sûre.

retries : nombre de tentatives

Échecs consécutifs avant le statut unhealthy.

Mécanisme anti-rebond : une jitter réseau ou une surcharge passagère ne doit pas déclencher unhealthy immédiatement.

3-5 est raisonnable. retries=1 trop sensible ; retries=10 trop lent.

start_period : période de grâce au démarrage (souvent oublié)

Paramètre le plus ignoré et le plus piégeux.

Les échecs pendant start_period ne comptent pas dans retries — une « période tampon » au démarrage.

Pourquoi c’est important ? Une base met du temps à démarrer. PostgreSQL initialise le répertoire de données, MySQL charge les index. Sans start_period, le healthcheck compte les échecs dès la 2e seconde ; le service peut être marqué unhealthy avant la fin des retries.

Valeurs recommandées :

  • PostgreSQL/MySQL : 30-60 s
  • Services légers (Redis) : 15-30 s
  • Initialisation lourde : jusqu’à 120 s

Je mets généralement 60 s — mieux vaut attendre un peu que déclencher un faux unhealthy.

Erreurs courantes et pièges

Erreur 1 : référence incorrecte aux variables d’environnement

# ❌ Compose interpole avant le démarrage — valeur de l'hôte, pas du conteneur
test: ["CMD", "mysqladmin", "ping", "-p$MYSQL_ROOT_PASSWORD"]

# ✅ $$ pour que le shell du conteneur interprète
test: ["CMD-SHELL", "mysqladmin ping -p$$MYSQL_ROOT_PASSWORD"]

Erreur 2 : start_period trop court

# ❌ La base n'est pas initialisée, compteur d'échecs rapide
healthcheck:
  test: ["CMD", "pg_isready"]
  interval: 5s
  retries: 3
  start_period: 10s  # trop court !

# ✅ Temps de démarrage suffisant
healthcheck:
  start_period: 60s  # bien plus serein

Erreur 3 : outil de vérification absent

Erreur discrète — Docker échoue silencieusement.

# ❌ Sans curl dans l'image, healthcheck échoue toujours
test: ["CMD", "curl", "-f", "http://localhost/health"]

# ✅ Outil présent ou outil natif de l'image
test: ["CMD", "wget", "--spider", "http://localhost/health"]  # wget inclus sur Alpine

Healthcheck configuré — voyons les bases de données concrètement.

Configuration healthcheck PostgreSQL en pratique

L’image officielle PostgreSQL inclut pg_isready.

Outil dédié à la vérification de disponibilité — plus fiable qu’une requête SQL maison.

Configuration de base (recommandée)

version: '3.8'

services:
  web:
    image: node:20-alpine
    depends_on:
      db:
        condition: service_healthy  # clé : attendre le passage du healthcheck
        restart: true  # redémarrage de l'app si la base redémarre
    environment:
      DATABASE_URL: postgresql://postgres:password@db:5432/myapp
    command: npm start

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myapp
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 60s
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Détails de la commande pg_isready

pg_isready -U postgres -d myapp
  • -U : nom d’utilisateur existant
  • -d : nom de la base (optionnel, mais recommandé)

Pourquoi -U ? Sans spécification, pg_isready tente l’utilisateur système courant — warnings dans les logs. Fonctionnel mais agaçant.

Configuration avancée : requête réelle

pg_isready vérifie la disponibilité du port, pas l’exécution de requêtes. Pour une vérification plus stricte :

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres && psql -U postgres -d myapp -c 'SELECT 1'"]
  interval: 10s
  timeout: 10s  # augmenter le timeout à cause de la requête
  retries: 3
  start_period: 60s

SELECT 1 est la requête la plus simple — succès = base démarrée ET capable d’exécuter du SQL.

Pour la plupart des cas, pg_isready seul suffit.

Effet au démarrage

Après configuration :

$ docker-compose up

Creating network "myapp_default" ... done
Creating myapp_db_1 ... done
Waiting for myapp_db_1 to be healthy... notez cette ligne
Creating myapp_web_1 ... done

db_1   | PostgreSQL init process complete; ready for start up.
db_1   | database system is ready to accept connections
web_1  | Server listening on port 3000 l'app démarre après que la base est prête

Pause visible — Docker attend que db passe à healthy. 30-60 secondes, mais démarrage sans échec.

Dépannage : conteneur bloqué en unhealthy

Si le conteneur reste unhealthy, consultez les logs de healthcheck :

# État de santé du conteneur
$ docker inspect --format='{{json .State.Health}}' myapp_db_1 | jq

{
  "Status": "unhealthy",
  "FailingStreak": 5,
  "Log": [
    {
      "Start": "2024-12-17T03:15:30Z",
      "End": "2024-12-17T03:15:30Z",
      "ExitCode": 1,
      "Output": "pg_isready: could not connect to server: Connection refused"
    }
  ]
}

Causes fréquentes :

  1. start_period trop court : la base initialise encore
  2. Utilisateur ou base incorrect : pg_isready ne peut pas se connecter
  3. Échec de démarrage PostgreSQL : voir docker logs myapp_db_1

Configuration healthcheck MySQL en pratique

Pour MySQL, utilisez mysqladmin ping — outil d’administration intégré.

Configuration de base (recommandée)

version: '3.8'

services:
  web:
    image: node:20-alpine
    depends_on:
      db:
        condition: service_healthy
        restart: true
    environment:
      DATABASE_URL: mysql://root:password@db:3306/myapp
    command: npm start

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: myapp
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ppassword"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 60s
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:

Détails de mysqladmin ping

mysqladmin ping -h localhost -u root -ppassword
  • -h : hôte (localhost dans le conteneur)
  • -u : utilisateur
  • -p : mot de passe (sans espace entre -p et le mot de passe)

Si MySQL fonctionne, la commande retourne :

mysqld is alive

Code de sortie 0 — healthcheck réussi.

Gestion correcte du mot de passe

Méthode 1 : mot de passe en dur (environnement de dev)

healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ppassword"]

Simple, mais mot de passe en clair dans la config.

Méthode 2 : variable d’environnement (recommandé)

db:
  environment:
    MYSQL_ROOT_PASSWORD: password
  healthcheck:
    test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"]
    # attention : $$ et non $

Piège : utiliser $$ et non $.

Pourquoi ? Docker Compose interpole les variables avant le démarrage. Avec $MYSQL_ROOT_PASSWORD, Compose cherche la variable sur l’hôte. $$ indique « laisser le shell du conteneur interpréter ».

Méthode 3 : sans mot de passe (le plus simple, controversé)

healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]

Certaines configs MySQL autorisent la connexion locale sans mot de passe. Non recommandé en production.

Particularités MySQL 8.0

MySQL 8.0 utilise par défaut le plugin caching_sha2_password, ce qui peut bloquer d’anciens clients. En cas d’erreur d’authentification :

db:
  image: mysql:8.0
  command: --default-authentication-plugin=mysql_native_password
  environment:
    MYSQL_ROOT_PASSWORD: password
    MYSQL_DATABASE: myapp
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ppassword"]
    interval: 10s
    timeout: 5s
    retries: 3
    start_period: 60s

Problèmes courants

Problème 1 : Access denied for user ‘root’@‘localhost’

Mot de passe incorrect ou variable non prise en compte. Vérifier :

  1. Orthographe de MYSQL_ROOT_PASSWORD
  2. Correspondance du mot de passe dans healthcheck
  3. Utilisation de $$ pour l’échappement

Problème 2 : démarrage lent, statut starting prolongé

L’initialisation du répertoire de données prend du temps, surtout au premier lancement. start_period ≥ 60 s. Scripts init volumineux : 120 s ou plus.

Problème 3 : healthcheck OK mais l’application ne se connecte pas

Problème réseau ou config applicative. Vérifier :

  1. Chaîne de connexion (hôte = db, pas localhost)
  2. Configuration réseau Docker
  3. docker network inspect — conteneurs sur le même réseau

Healthcheck pour d’autres bases et services

PostgreSQL et MySQL maîtrisés, les autres services suivent la même logique. Tableau de référence rapide.

Redis

redis:
  image: redis:7-alpine
  healthcheck:
    test: ["CMD", "redis-cli", "ping"]
    interval: 10s
    timeout: 3s
    retries: 3
    start_period: 15s

redis-cli ping retourne PONG, code 0. Redis démarre vite — start_period court.

MongoDB

mongo:
  image: mongo:7
  healthcheck:
    test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
    interval: 10s
    timeout: 5s
    retries: 3
    start_period: 30s

Note : MongoDB 6.0+ utilise mongosh à la place de mongo. Versions antérieures :

test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]

RabbitMQ

rabbitmq:
  image: rabbitmq:3-management-alpine
  healthcheck:
    test: ["CMD", "rabbitmq-diagnostics", "ping"]
    interval: 10s
    timeout: 5s
    retries: 3
    start_period: 40s

RabbitMQ démarre lentement — start_period ≥ 40 s recommandé.

Service HTTP générique

Si le service expose un endpoint de santé (/health ou /ping), utilisez wget ou curl :

api:
  image: myapp:latest
  healthcheck:
    test: ["CMD", "wget", "--spider", "--quiet", "http://localhost:8080/health"]
    # ou curl (si présent dans l'image)
    # test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
    interval: 10s
    timeout: 3s
    retries: 3
    start_period: 20s

--spider : wget vérifie sans télécharger ; --quiet : pas de sortie.

Attention : wget ou curl doit exister dans l’image. Alpine a wget par défaut ; Debian/Ubuntu ont curl.

Services sans outil dédié

Sans outil de healthcheck, netcat (nc) pour vérifier le port :

service:
  image: some-service:latest
  healthcheck:
    test: ["CMD-SHELL", "nc -z localhost 9000 || exit 1"]
    interval: 10s
    timeout: 3s
    retries: 3
    start_period: 30s

nc -z vérifie l’ouverture du port sans connexion réelle — moins précis que les méthodes précédentes.

wait-for-it : encore nécessaire en 2024 ?

En cherchant l’ordre de démarrage Docker, vous trouverez des articles recommandant wait-for-it ou wait-for.

Principe : ajouter une logique d’attente dans l’entrypoint de l’application, vérifier que le port TCP de la dépendance est accessible.

Approche wait-for-it traditionnelle

web:
  image: node:20-alpine
  depends_on:
    - db  # depends_on simple, sans vérification de santé
  volumes:
    - ./wait-for-it.sh:/wait-for-it.sh  # montage du script
  command: ["/wait-for-it.sh", "db:5432", "--", "npm", "start"]

wait-for-it.sh boucle sur db:5432 jusqu’à connexion, puis exécute npm start.

Pourquoi ce n’est plus recommandé ?

Meilleure pratique 2024 : préférer le healthcheck natif aux scripts.

Raisons :

  1. Config plus claire : la logique de santé est dans le service base, dépendances visibles
  2. Pas de fichier supplémentaire : pas de script à maintenir ni volume à monter
  3. Plus robuste : healthcheck vérifie la disponibilité réelle ; le port TCP seul est grossier
  4. Meilleure réutilisabilité : healthcheck configuré une fois, tous les services dépendants en profitent

Un article populaire s’intitule « Forget wait-for-it, use docker-compose healthcheck and depends_on instead ».

Quand wait-for-it reste utile ?

Deux cas particuliers :

Cas 1 : impossible de modifier l’image ou la config compose

Image tierce sans healthcheck, sans droit d’ajout. wait-for-it côté application est la seule option.

Cas 2 : attendre plusieurs services

./wait-for-it.sh db:5432 redis:6379 rabbitmq:5672 -- npm start

depends_on gère aussi plusieurs dépendances, mais wait-for-it est plus concis. Cas rare.

Autres outils alternatifs

  • dockerize : Go, plus riche, templates de variables d’environnement
  • wait-for : version simplifiée de wait-for-it, shell pur
  • docker-compose-wait : Python, support HTTP

En 2024, si healthcheck suffit, inutile de compliquer.

Checklist de dépannage et bonnes pratiques

Config faite mais ça ne marche pas ? Suivez cette checklist — 90 % des problèmes se résolvent.

Commandes de diagnostic rapide

# 1. État de tous les conteneurs
$ docker-compose ps

NAME       COMMAND    SERVICE   STATUS              PORTS
myapp_db   postgres   db        healthy             5432/tcp
myapp_web  npm start  web       running             0.0.0.0:3000->3000/tcp

# 2. Détails du healthcheck
$ docker inspect --format='{{json .State.Health}}' myapp_db_1 | jq

# 3. Logs des conteneurs
$ docker-compose logs db
$ docker-compose logs web

# 4. Suivi en temps réel
$ docker-compose logs -f --tail=100

Arbre de décision pour problèmes courants

Problème : conteneur bloqué en starting, jamais healthy

  1. start_period trop court → essayer 60 s
  2. Commande de healthcheck incorrecte → docker inspect pour voir la commande réelle
  3. Exécuter manuellement dans le conteneur → docker exec -it myapp_db_1 pg_isready -U postgres

Problème : unhealthy puis healthy en boucle

  1. interval trop court, ressources insuffisantes → 10 s ou 15 s
  2. retries trop bas, faux positifs → 5
  3. Problème de performance base → consulter les logs

Problème : healthcheck OK mais l’application ne se connecte pas

  1. Chaîne de connexion correcte → hôte = nom du service (db), pas localhost
  2. Port correct → communication inter-conteneurs sur le port interne (5432), pas le port mappé
  3. Réseau → tous les services sur le même network

Bonnes pratiques en production

1. Valeurs recommandées (config conservative)

healthcheck:
  interval: 10s          # équilibre réactivité / ressources
  timeout: 5s            # temps suffisant pour la commande
  retries: 5             # tolérance aux pannes passagères
  start_period: 60s      # temps de démarrage base suffisant

Stable dans la plupart des cas. Initialisation lourde : start_period à 120 s.

2. Stratégie restart

web:
  depends_on:
    db:
      condition: service_healthy
      restart: true  # redémarrage de l'app si la base redémarre
  restart: unless-stopped  # redémarrage automatique après sortie

restart: true garantit que les services dépendants redémarrent et se reconnectent après une mise à jour ou un redémarrage de la base.

3. Limites de ressources

Le healthcheck consomme peu. Si les ressources sont tendues :

healthcheck:
  interval: 30s  # intervalle plus long
  timeout: 3s    # timeout plus court

En pratique, l’overhead est négligeable sauf avec des centaines de conteneurs.

4. Surveillance de l’état de santé

En production, un outil de monitoring (Prometheus, Grafana) peut collecter les événements de healthcheck.

# docker-compose.yml
services:
  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 60s
    labels:
      - "prometheus.io/scrape=true"  # collecte Prometheus

5. Configuration multi-environnement

Dev et prod peuvent différer :

# docker-compose.yml (dev)
db:
  healthcheck:
    start_period: 30s  # peu de données en dev, démarrage rapide

# docker-compose.prod.yml (prod)
db:
  healthcheck:
    start_period: 120s  # plus de données, démarrage lent
    interval: 5s        # vérifications plus fréquentes

Lancement avec fichiers multiples :

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up

Astuces de débogage

Astuce 1 : tester manuellement la commande de healthcheck

Entrer dans le conteneur et exécuter la commande :

$ docker exec -it myapp_db_1 sh
/# pg_isready -U postgres -d myapp
/var/run/postgresql:5432 - accepting connections
/# echo $?
0 code 0 = succès

Astuce 2 : désactiver temporairement le healthcheck

Commenter healthcheck, utiliser un depends_on simple pour exclure un problème de healthcheck :

web:
  depends_on:
    - db  # mode simple temporaire
    # db:
    #   condition: service_healthy

Une fois la connexion confirmée, réactiver healthcheck.

Astuce 3 : événements Docker

Docker enregistre tous les événements, y compris les changements de healthcheck :

$ docker events --filter 'event=health_status'

2024-12-17T03:15:30.123456789Z container health_status: healthy (name=myapp_db_1)
2024-12-17T03:16:45.987654321Z container health_status: unhealthy (name=myapp_db_1)

Utile pour localiser le moment du passage à unhealthy.

Conclusion

Retour à la question du début : pourquoi depends_on ne suffit pas ?

Réponse en trois mots : pas assez.

depends_on par défaut ne gère que le démarrage du conteneur, pas la disponibilité du service. Un conteneur base en running n’accepte pas forcément les connexions — ce décalage est la source des échecs.

La solution est directe :

  1. Ajouter healthcheck à la base avec pg_isready ou mysqladmin ping
  2. Utiliser condition: service_healthy pour que l’application attende le passage du healthcheck
  3. Définir un start_period raisonnable (60 s minimum) pour l’initialisation

Avec ces trois points, les échecs de démarrage disparaissent en général.

Quelques actions rapides :

  • Immédiat : copier la config PostgreSQL ou MySQL de l’article, adapter les variables d’environnement
  • Ce soir : migrer tous les depends_on de l’équipe vers service_healthy
  • Prochaine réunion : partager avec les collègues pour unifier les standards

Si vous rencontrez un cas non couvert, laissez un commentaire. Docker Compose a encore beaucoup de pièges — remplissons-les ensemble.

Si cet article vous a aidé à résoudre un problème de démarrage tenace, un like me le fera savoir. Ce moment « enfin c’est bon », c’est ce qui me motive à écrire.

Flux complet de configuration healthcheck Docker Compose

Résoudre les échecs de démarrage dus à une base non prête, avec modèles complets PostgreSQL et MySQL

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Comprendre la cause : les limites de depends_on

    Cause racine :
    • depends_on ne gère que l'ordre de démarrage des conteneurs, pas si le service est réellement prêt
    • Compose attend que le conteneur tourne, pas qu'il soit utilisable
    • Documentation Docker : Compose does not wait until a container is "ready", only until it's running
    • Traduction : Compose attend que le conteneur tourne, pas qu'il soit utilisable

    Décalage entre démarrage du conteneur et service prêt :
    • 0 s : Docker lance le conteneur, processus postgres démarre (depends_on libère ici)
    • 2 s : initialisation du répertoire de données
    • 5 s : chargement de la configuration
    • 8 s : exécution des scripts init (le cas échéant)
    • 12 s : enfin prêt, accepte les connexions

    Entre les deux, 12 secondes de décalage. Si votre application web tente de se connecter à la base à la 1re seconde, le résultat est Connection refused.
  2. 2

    Step 2: Les trois conditions de depends_on

    Trois conditions de depends_on :

    1. service_started (défaut)
    • Attend seulement le démarrage du conteneur, pas la disponibilité du service
    • Non recommandé

    2. service_healthy (recommandé)
    • Attend le démarrage du conteneur ET le passage du healthcheck
    • Garantit que le service est réellement prêt

    3. service_completed_successfully
    • Attend la fin d'exécution du conteneur avec succès
    • Adapté aux tâches ponctuelles

    Recommandation service_healthy :
    • Dans docker-compose.yml : depends_on: db: condition: service_healthy
    • Garantit que la base est prête avant le démarrage de l'application
  3. 3

    Step 3: Healthcheck correct pour PostgreSQL et MySQL

    Healthcheck PostgreSQL :
    • Utiliser pg_isready pour vérifier si la base est prête
    • Exemple :
    healthcheck:
    test: ["CMD-SHELL", "pg_isready -U postgres"]
    interval: 5s
    timeout: 5s
    retries: 5
    start_period: 60s (temps suffisant pour l'initialisation)

    Healthcheck MySQL :
    • Utiliser mysqladmin ping pour vérifier si la base est prête
    • Exemple :
    healthcheck:
    test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$MYSQL_ROOT_PASSWORD"]
    interval: 5s
    timeout: 5s
    retries: 5
    start_period: 60s

    Exemple complet :
    • Configurer healthcheck dans docker-compose.yml (commande, intervalle, timeout, retries)
    • Utiliser condition: service_healthy dans depends_on
    • Garantir que la base est prête avant le démarrage de l'application
  4. 4

    Step 4: Dépannage et bonnes pratiques

    Checklist de dépannage :
    1. Vérifier que la commande de healthcheck est correcte
    • Exécuter manuellement la commande dans le conteneur, confirmer le code de retour 0
    • Exemple : docker exec -it db_container pg_isready -U postgres

    2. Désactiver temporairement le healthcheck
    • Commenter healthcheck pour le débogage
    • Utiliser un depends_on simple pour exclure un problème de healthcheck

    3. Consulter les événements Docker
    • docker events pour voir démarrage et healthcheck

    Bonnes pratiques :
    1. Ajouter healthcheck à la base avec pg_isready ou mysqladmin ping
    2. Utiliser condition: service_healthy pour que l'application attende le passage du healthcheck
    3. Définir un start_period raisonnable (60 s minimum) pour l'initialisation

    Avec ces trois points, les échecs de démarrage disparaissent en général.

    Alternative moderne à wait-for-it :
    • Combinaison depends_on + healthcheck
    • Pas de script supplémentaire, configuration simple, fiabilité élevée
    • Adapté à tous les projets Docker Compose

FAQ

Pourquoi depends_on ne suffit-il pas ? Quelle différence entre démarrage du conteneur et service prêt ?
Cause racine : depends_on ne gère que l'ordre de démarrage des conteneurs, pas si le service est réellement prêt. Compose attend que le conteneur tourne, pas qu'il soit utilisable.

La documentation Docker contient une phrase importante souvent ignorée : Compose does not wait until a container is "ready", only until it's running. Traduction : Compose attend que le conteneur tourne, pas qu'il soit utilisable.

Décalage entre démarrage et disponibilité — imaginez le démarrage d'un conteneur PostgreSQL :
• 0 s : Docker lance le conteneur, processus postgres démarre (depends_on libère ici)
• 2 s : initialisation du répertoire de données
• 5 s : chargement de la configuration
• 8 s : exécution des scripts init (le cas échéant)
• 12 s : enfin prêt, accepte les connexions

Entre les deux, 12 secondes de décalage. Si votre application web tente de se connecter à la 1re seconde, le résultat est Connection refused.
Quelles sont les trois conditions de depends_on ?
Trois conditions de depends_on :

1) service_started (défaut) :
• Attend seulement le démarrage du conteneur, pas la disponibilité du service
• Non recommandé

2) service_healthy (recommandé) :
• Attend le démarrage du conteneur ET le passage du healthcheck
• Garantit que le service est réellement prêt
• Dans docker-compose.yml : depends_on: db: condition: service_healthy

3) service_completed_successfully :
• Attend la fin d'exécution du conteneur avec succès
• Adapté aux tâches ponctuelles

Recommandation service_healthy : garantir que la base est prête avant le démarrage de l'application.
Comment configurer le healthcheck pour PostgreSQL et MySQL ?
Healthcheck PostgreSQL :
• Utiliser pg_isready pour vérifier si la base est prête
• Exemple :
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
start_period: 60s (temps suffisant pour l'initialisation)

Healthcheck MySQL :
• Utiliser mysqladmin ping pour vérifier si la base est prête
• Exemple :
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$MYSQL_ROOT_PASSWORD"]
interval: 5s
timeout: 5s
retries: 5
start_period: 60s

Exemple complet : configurer healthcheck dans docker-compose.yml (commande, intervalle, timeout, retries), utiliser condition: service_healthy dans depends_on, garantir que la base est prête avant le démarrage de l'application.
Comment dépanner les problèmes de healthcheck ?
Checklist de dépannage :

1) Vérifier la commande de healthcheck :
• Exécuter manuellement la commande dans le conteneur, confirmer le code de retour 0
• Exemple : docker exec -it db_container pg_isready -U postgres
• Code de retour 0 = succès

2) Désactiver temporairement le healthcheck :
• Commenter healthcheck pour le débogage
• Utiliser un depends_on simple pour exclure un problème de healthcheck
• Une fois la connexion confirmée, réactiver healthcheck

3) Consulter les événements Docker :
• docker events pour voir démarrage et healthcheck
Quelles sont les bonnes pratiques pour le healthcheck ?
Bonnes pratiques :
1) Ajouter healthcheck à la base avec pg_isready ou mysqladmin ping
2) Utiliser condition: service_healthy pour que l'application attende le passage du healthcheck
3) Définir un start_period raisonnable (60 s minimum) pour l'initialisation

Avec ces trois points, les échecs de démarrage disparaissent en général.

Alternative moderne à wait-for-it :
• Combinaison depends_on + healthcheck
• Pas de script supplémentaire, configuration simple, fiabilité élevée
• Adapté à tous les projets Docker Compose

Actions rapides :
• Immédiat : copier la config PostgreSQL ou MySQL de l'article dans votre projet, adapter les variables d'environnement
• Ce soir : migrer tous les depends_on de l'équipe vers service_healthy
• Prochaine réunion d'équipe : partager avec les collègues pour unifier les standards

14 min de lecture · Publié le: 17 déc. 2025 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog