Déploiement Docker Compose en production : healthcheck, redémarrage et journaux

Les alertes SMS s’enchaînent sur le téléphone. Vous ouvrez le terminal : le disque est à 99 % — les journaux des conteneurs occupent 50 Go.
Ce n’est pas le pire scénario. L’an dernier, sur un projet, le conteneur API affichait « running », mais la connexion à la base était coupée depuis longtemps ; toutes les requêtes renvoyaient 500. Trois heures de diagnostic pour trouver la cause. Selon une étude Last9, une « mort apparente » de conteneur coûte en moyenne 3,2 heures de investigation.
Beaucoup d’équipes déploient Docker Compose en production pour la première fois avec seulement un mapping de ports et des volumes, puis lancent les conteneurs. Pas de healthcheck, pas de rotation des journaux, et un restart: always au hasard. Résultat : le conteneur semble tourner alors qu’il est déjà mort ; les fichiers de log grossissent jusqu’à saturer le disque ; un service en crash redémarre en boucle et épuise CPU et mémoire.
Cet article détaille les trois réglages essentiels en production — healthcheck, politique de redémarrage, gestion des journaux — avec des exemples, des commandes de vérification par type de service, des étapes de dépannage et un modèle docker-compose.yml prêt à copier.
Healthcheck — s’assurer que le conteneur est vraiment vivant
Un statut running ne prouve pas que l’application fonctionne. Base injoignable, port non écouté, processus bloqué : Docker ne le détecte pas seul. Le healthcheck agit comme un « moniteur de pouls » qui vérifie périodiquement que l’application répond encore correctement.
Syntaxe de configuration
Dans docker-compose.yml, un bloc healthcheck ressemble à ceci :
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s # vérification toutes les 10 secondes
timeout: 5s # chaque test attend au plus 5 secondes
retries: 5 # 5 échecs consécutifs avant unhealthy
start_period: 30s # 30 secondes de grâce au démarrage
Ces paramètres doivent être cohérents. timeout ne doit pas dépasser interval, sinon la vérification suivante démarre avant la fin de la précédente. Ne supprimez pas start_period : une base met du temps à démarrer ; un délai trop court provoque des faux positifs.
Commandes de healthcheck par service courant
Chaque service se vérifie différemment. Exemples fréquents :
PostgreSQL
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
pg_isready est l’outil natif PostgreSQL pour savoir si la base accepte les connexions.
MySQL / MariaDB
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
Échappez le mot de passe avec $$, sinon YAML interprète $ comme une variable.
Redis
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 3s
retries: 3
La commande ping de Redis renvoie PONG ; grep confirme le résultat attendu.
Serveur Web (vérification HTTP)
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
L’option -f fait échouer curl si le code HTTP n’est pas 2xx, ce qui déclenche l’échec du healthcheck.
Piège : les images Alpine n’ont souvent pas curl. Installez-le (apk add curl) ou utilisez wget :
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"]
Contrôle de l’ordre de démarrage
La base n’est pas prête, l’API démarre déjà, échec de connexion, crash — on l’a tous vu. depends_on avec condition: service_healthy règle le problème :
services:
postgres:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
api:
build: ./api
depends_on:
postgres:
condition: service_healthy # l'API démarre quand postgres est healthy
Docker Compose attend que postgres soit healthy avant de lancer api. Fini le « la base n’est pas prête et l’API se connecte quand même ».
Politique de redémarrage — récupération maîtrisée après échec
Un conteneur planté, on veut le relancer automatiquement. Mais si la cause profonde reste, le redémarrage en boucle gaspille CPU et mémoire et masque la panne réelle.
Syntaxe de configuration
La politique se configure dans le bloc deploy :
deploy:
restart_policy:
condition: on-failure # redémarrer seulement en cas d'échec
delay: 5s # attendre 5 s avant de redémarrer
max_attempts: 3 # au plus 3 tentatives
window: 120s # succès si stable 120 s après redémarrage
condition propose trois valeurs :
none: pas de redémarrage automatiqueon-failure: redémarrage si sortie anormale (code ≠ 0)any: redémarrage dans tous les cas
Recommandation en production
En production, préférez on-failure à always.
Pourquoi ? restart: always relance le conteneur quelle que soit la cause : bug applicatif, base injoignable, mauvaise config. Boucle de crash, journaux saturés, CPU épuisé.
Avec on-failure et max_attempts, Docker s’arrête après quelques tentatives. L’exploitation voit le conteneur arrêté et peut traiter la cause racine.
Réglage des paramètres
delay est l’intervalle entre tentatives. Trop court : le conteneur redémarre avant un nettoyage complet ; trop long : récupération lente. Souvent 5 à 10 secondes conviennent.
window est souvent négligé : durée pendant laquelle le conteneur doit rester stable pour compter le redémarrage comme réussi. Avec window: 120s, un nouveau crash dans les 120 s ne réinitialise pas le compteur max_attempts, ce qui évite de compter comme « réussi » un service qui replante aussitôt.
Healthcheck et redémarrage ensemble
Les deux mécanismes travaillent en chaîne :
- Échecs consécutifs du healthcheck (
retries) → conteneurunhealthy - Avec
restart_policy, Docker tente un redémarrage - Le healthcheck repart à zéro après redémarrage
- Si le healthcheck passe, retour à la normale ; sinon nouvelles tentatives jusqu’à
max_attempts
Vous obtenez une récupération automatique limitée, sans boucle infinie.
Gestion des journaux — éviter de saturer le disque
L’alerte à 3 h du matin, disque à 99 %, 50 Go de logs — ça m’est arrivé plus d’une fois. Le driver json-file par défaut ne purge pas les anciens fichiers ; sans rotation, le disque finit par être plein.
Configuration de la rotation
Ajoutez un bloc logging dans docker-compose.yml :
logging:
driver: "json-file"
options:
max-size: "10m" # 10 Mo max. par fichier
max-file: "3" # 3 fichiers conservés
compress: "true" # compression des anciens fichiers
Au total, environ 30 Mo (10 Mo × 3). Au-delà de 10 Mo, Docker crée un nouveau fichier ; au-delà de 3 fichiers, le plus ancien est supprimé ou compressé.
Les fichiers sont sous /var/lib/docker/containers/<container-id>/<container-id>-json.log. Vérifiez l’occupation avec :
du -sh /var/lib/docker/containers/*/*-json.log
Choix du driver
Docker propose json-file, syslog, fluentd, journald, local, etc. Pour la plupart des cas, json-file ou local suffit.
La documentation officielle indique que local est plus efficace que json-file et gère la rotation sans max-size/max-file. Pour des volumes énormes (dizaines de Go par jour), envisagez local :
logging:
driver: "local"
Inconvénient : docker logs ne lit pas directement les journaux local ; il faut souvent mode: "non-blocking" dans la config pour la compatibilité.
Collecte centralisée (optionnel)
En mono-serveur, json-file ou local suffit. Avec des dizaines de machines et des centaines de conteneurs, une solution centralisée aide :
- Fluentd : collecte légère, adaptée aux petits clusters
- ELK Stack (Elasticsearch + Logstash + Kibana) : puissant, coût de déploiement élevé
- Loki + Grafana : approche cloud native, bonne intégration Prometheus
La configuration détaillée dépasse ce article. Exemple d’idée avec Fluentd :
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "docker.{{.Name}}"
Fluentd envoie les journaux vers l’adresse configurée pour analyse centralisée.
Modèle de configuration complet
En combinant healthcheck, redémarrage et journaux, vous obtenez un docker-compose.yml de niveau production. Exemple avec PostgreSQL, Redis et une API :
version: '3.8'
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
api:
build:
context: ./api
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://myuser:mypassword@postgres:5432/mydb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
deploy:
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
compress: "true"
volumes:
postgres_data:
Points clés du modèle
Ordre de démarrage : l’API attend que postgres et redis passent en healthy. Base et cache prêts avant les connexions de l’application.
Taille des journaux : postgres et redis : 10 Mo × 3 en général ; API plus verbeuse : 50 Mo × 5. Ajustez selon le volume réel.
Délai de redémarrage : postgres lent au boot → delay 5 s ; API plus rapide → delay 10 s pour laisser le healthcheck respirer.
start_period : postgres 30 s pour l’init ; redis 5 s ; API 10 s selon le temps de boot habituel.
Copiez le modèle, adaptez variables et images. Pour MongoDB, MinIO, etc., appliquez le même trio healthcheck / redémarrage / journaux.
Pièges courants et dépannage
Même avec une bonne config, des problèmes peuvent survenir. Pièges fréquents et pistes de diagnostic.
Healthcheck en échec permanent
Symptôme : statut unhealthy alors que l’application semble OK.
Étapes :
-
Vérifiez la présence des outils :
docker exec <container> which curl docker exec <container> which pg_isreadySur Alpine, installez curl ou passez à wget.
-
Exécutez la commande du healthcheck à la main :
docker exec <container> curl -f http://localhost:8080/healthUne erreur peut venir de l’endpoint
/healthlui-même. -
Détail de l’état :
docker inspect --format='{{json .State.Health}}' <container> | jqHistorique des tests, causes et horodatages.
Redémarrages en boucle
Symptôme : le conteneur redémarre en permanence, journaux pleins de relances.
Étapes :
-
Code de sortie et message d’erreur :
docker inspect --format='{{.State.ExitCode}}' <container> docker inspect --format='{{.State.Error}}' <container>Codes utiles : 1 = erreur générale, 137 = OOM, 139 = segmentation fault.
-
Nombre de redémarrages :
docker inspect --format='{{.RestartCount}}' <container>Si le compteur est très élevé, vérifiez que
max_attemptss’applique. -
Journaux récents :
docker logs --tail 100 <container>
Disque plein à cause des journaux
Symptôme : alerte espace disque, gros répertoire /var/lib/docker/containers.
Étapes :
-
Fichiers les plus volumineux :
du -sh /var/lib/docker/containers/*/*-json.log | sort -rh | head -5 -
Rotation effective ou non :
docker inspect --format='{{.HostConfig.LogConfig}}' <container>Config: {}signifie souvent l’absence de rotation. -
Vidage temporaire (dépannage uniquement) :
truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.logÀ long terme, configurez la rotation dans compose.
Commandes de diagnostic rapide
# État de santé de tous les conteneurs
docker ps --format "table {{.Names}}\t{{.Status}}"
# Historique du healthcheck d'un conteneur
docker inspect --format='{{json .State.Health}}' <container>
# Code de sortie et nombre de redémarrages
docker inspect --format='ExitCode: {{.State.ExitCode}}, RestartCount: {{.RestartCount}}' <container>
# Taille des fichiers de journaux
du -sh /var/lib/docker/containers/*/*-json.log | sort -rh
# 100 dernières lignes de journaux
docker logs --tail 100 <container>
Synthèse
En production, ces trois réglages ne sont pas optionnels : le healthcheck évite un conteneur « qui semble vivant », la politique de redémarrage permet une récupération limitée, la gestion des journaux protège le disque.
Liste des réglages essentiels :
- Healthcheck :
test+interval+timeout+retries+start_period - Redémarrage :
condition: on-failure+max_attempts: 3 - Rotation des journaux :
max-size: 10m+max-file: 3+compress: true
Plan en trois étapes :
- Ouvrez votre docker-compose.yml actuel : ajoutez au minimum healthcheck et rotation des journaux s’ils manquent.
- Déployez un service de test avec le modèle ci-dessus ; vérifiez healthcheck et rotation.
- Gardez les commandes de dépannage sous la main pour la prochaine alerte nocturne.
Ne laissez pas vos conteneurs « à nu » en production. Avec ces trois protections, vous gagnez récupération automatique, diagnostic plus rapide et disque préservé.
FAQ
Comment régler interval et timeout du healthcheck de façon raisonnable ?
restart: always ou on-failure en production ?
Quelle taille et combien de fichiers de journaux conserver ?
Le healthcheck échoue en boucle alors que l'application semble OK : que faire ?
À quoi sert depends_on avec condition: service_healthy ?
Comment voir rapidement l'espace disque pris par les journaux des conteneurs ?
10 min de lecture · Publié le: 12 avr. 2026 · Mis à jour le: 27 juil. 2026
Guide pratique Docker
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Dépannage Docker Compose : 5 erreurs courantes et solutions rapides
Erreur Docker Compose ? Ce guide couvre conflits de ports, réseau, échecs de build, sortie de conteneur et permissions, avec un flux de diagnostic systématique pour localiser le problème en 5 minutes.
Partie 10 sur 38
Suivant
Déploiement Docker Compose en production : trois piliers — healthcheck, restart et limites de ressources
Trois piliers du déploiement Docker Compose en production : healthcheck pour vérifier la disponibilité, politique de redémarrage pour l'auto-réparation, limites de ressources pour éviter les débordements. Modèle YAML complet inclus.
Partie 12 sur 38



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire