Docker : résoudre les problèmes de conteneurs qui redémarrent en boucle

Un conteneur Docker qui redémarre continuellement est un problème fréquent lors du déploiement d’une application. Le service semble démarrer, puis s’arrête quelques secondes plus tard. Docker tente alors de le relancer, ce qui peut donner l’impression que l’application fonctionne par intermittence.

Ce comportement peut être provoqué par une erreur dans l’application, une variable d’environnement manquante, un fichier inaccessible, une dépendance indisponible ou une politique de redémarrage.

La première règle est simple : ne modifiez pas la configuration au hasard. Commencez par observer le conteneur et ses journaux.

1. Identifier le conteneur concerné

Affichez les conteneurs actifs :

docker ps

Si le conteneur n’apparaît pas, affichez également ceux qui sont arrêtés :

docker ps -a

Examinez les colonnes STATUS, NAMES et IMAGE.

Un état tel que Restarting indique que le conteneur est en cours de redémarrage répété. Un état Exited signifie que son processus principal s’est terminé.

Pour faciliter les commandes suivantes, utilisez le nom ou l’identifiant réel du conteneur.

2. Lire les journaux avec docker logs

La commande la plus utile pour commencer est :

docker logs --tail 100 NOM_DU_CONTENEUR

Remplacez NOM_DU_CONTENEUR par le nom affiché par docker ps -a.

Pour suivre les nouveaux messages :

docker logs -f NOM_DU_CONTENEUR

Les journaux peuvent révéler plusieurs problèmes :

  • une variable d’environnement obligatoire est absente ;
  • l’application ne parvient pas à se connecter à une base de données ;
  • un fichier de configuration est introuvable ;
  • le port demandé est déjà utilisé ;
  • l’application rencontre une exception au démarrage.

Si les journaux sont vides, cela ne prouve pas que Docker fonctionne mal. L’application peut ne rien écrire sur la sortie standard ou quitter avant de produire un message exploitable. Il faut alors examiner la configuration du conteneur.

3. Examiner le code de sortie et l’état du conteneur

Docker conserve des informations sur la dernière exécution du conteneur.

Pour afficher son état :

docker inspect --format='Status={{.State.Status}} ExitCode={{.State.ExitCode}} Error={{.State.Error}} OOMKilled={{.State.OOMKilled}}' NOM_DU_CONTENEUR

Les résultats permettent de mieux orienter le diagnostic.

ExitCode indique le code de sortie du processus principal. Une valeur non nulle signale souvent une erreur, mais son interprétation dépend de l’application.

OOMKilled=true indique que le conteneur a été arrêté parce qu’il a été tué à la suite d’un problème de mémoire, généralement par le mécanisme OOM du noyau.

Error peut contenir des informations sur une erreur détectée par Docker, mais ce champ n’explique pas nécessairement les erreurs internes de l’application.

Un code de sortie égal à zéro ne signifie pas toujours que le service fonctionne correctement. Si le processus principal se termine normalement, Docker peut quand même redémarrer le conteneur selon sa politique de redémarrage.

4. Vérifier les variables d’environnement

De nombreuses applications ont besoin de variables d’environnement pour fonctionner : adresse de la base de données, port d’écoute, mode d’exécution ou paramètres d’authentification.

Dans un fichier Compose, ces variables peuvent être définies directement dans environment ou chargées depuis un fichier .env.

Exemple :

services:
  app:
    image: mon-application:latest
    environment:
      APP_ENV: production
      APP_PORT: "3000"

Les noms et les valeurs ci-dessus sont illustratifs : ils doivent correspondre à ce que votre application attend réellement.

Vérifiez également que le fichier .env est présent au bon endroit et que les noms de variables correspondent exactement à ceux utilisés par l’application.

Évitez de publier la sortie complète de docker inspect sans vérification : la configuration peut contenir des mots de passe, des jetons ou d’autres secrets.

5. Contrôler les volumes et les permissions

Un conteneur peut échouer si un fichier attendu est absent ou si l’application ne dispose pas des permissions nécessaires pour lire ou écrire dans un répertoire.

Pour examiner les montages :

docker inspect --format='{{json .Mounts}}' NOM_DU_CONTENEUR

Cette commande affiche les volumes et les répertoires associés au conteneur.

Si vous utilisez Docker Compose, examinez la section volumes du service concerné. Vérifiez que le chemin source existe sur l’hôte et que le chemin de destination correspond à celui attendu par l’application.

Une erreur fréquente consiste à monter un répertoire vide sur un répertoire qui contenait des fichiers nécessaires dans l’image. Le contenu d’origine peut alors devenir invisible dans le conteneur.

Ne supprimez pas un volume Docker pour tenter de résoudre le problème : il peut contenir une base de données ou des fichiers persistants.

6. Vérifier les connexions aux autres services

Une application peut démarrer puis quitter parce qu’elle ne parvient pas à joindre sa base de données, son serveur de cache ou une API interne.

Avec Docker Compose, les services d’un même réseau peuvent généralement communiquer en utilisant leur nom de service comme nom d’hôte.

Par exemple :

services:
  app:
    image: mon-application:latest
    environment:
      DATABASE_HOST: db

  db:
    image: postgres:16

Dans cet exemple, db est le nom du service PostgreSQL. L’application doit toutefois utiliser le protocole, le port, les identifiants et les paramètres attendus par sa configuration.

Vérifiez que la base de données est bien démarrée, que les identifiants sont corrects et que le service est prêt à accepter les connexions. Le simple fait qu’un conteneur de base de données soit actif ne garantit pas que son initialisation est terminée.

7. Examiner les ressources mémoire et processeur

Un conteneur peut être arrêté si le système manque de mémoire ou si des limites de ressources sont trop strictes.

Pour examiner la consommation des conteneurs actifs :

docker stats

Pour vérifier si le dernier arrêt est lié à la mémoire, utilisez la commande docker inspect présentée précédemment et examinez la valeur OOMKilled.

Vous pouvez également consulter les messages du noyau, si vous disposez des permissions nécessaires :

sudo journalctl -k --since "30 minutes ago"

Cherchez notamment les messages liés au manque de mémoire.

N’augmentez pas automatiquement les limites. Commencez par vérifier la consommation réelle, les besoins de l’application et les ressources disponibles sur l’hôte.

8. Comprendre la politique de redémarrage

Docker propose plusieurs politiques de redémarrage, notamment no, on-failure, always et unless-stopped.

Ces politiques déterminent dans quelles circonstances Docker tente de relancer un conteneur. Elles ne corrigent pas la cause de son arrêt.

Pour examiner la politique actuelle :

docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' NOM_DU_CONTENEUR

Si un conteneur redémarre en boucle, désactiver temporairement sa politique de redémarrage peut faciliter le diagnostic, mais cette action doit être effectuée en connaissance de cause.

Ne changez pas la politique d’un service de production sans comprendre ses conséquences opérationnelles. Le bon objectif est de corriger la cause de l’arrêt, puis de conserver une politique adaptée au service.

9. Vérifier la configuration Docker Compose

Si vous utilisez Docker Compose, validez d’abord le fichier de configuration :

docker compose config

Cette commande permet de détecter certaines erreurs de syntaxe et de voir la configuration Compose résolue. Selon les versions et les options utilisées, la sortie peut contenir des informations sensibles : ne la partagez pas publiquement sans la vérifier.

Pour afficher les journaux d’un service défini dans le projet :

docker compose logs --tail 100 app

Remplacez app par le nom réel du service dans le fichier Compose.

Une fois la cause identifiée et la configuration corrigée, vous pouvez recréer ou redémarrer le service selon les changements effectués. Avant toute opération destructive, vérifiez les volumes et les données persistantes.

Exemple pratique : une application redémarre après son lancement

Imaginons qu’une application Node.js démarre puis s’arrête parce qu’elle ne parvient pas à se connecter à PostgreSQL.

Commencez par :

docker ps -a
docker logs --tail 100 mon-application

Si les journaux indiquent un échec de connexion, vérifiez :

  1. que le service PostgreSQL est actif ;
  2. que l’application utilise le bon nom d’hôte ;
  3. que le port et les identifiants sont corrects ;
  4. que les deux services peuvent communiquer sur le réseau Docker ;
  5. que la base de données est prête à accepter les connexions.

Corrigez ensuite le paramètre concerné et vérifiez les journaux à nouveau.

Cette méthode permet d’éviter les redémarrages inutiles et de concentrer les efforts sur la cause réelle.

FAQ

Pourquoi un conteneur Docker redémarre-t-il en boucle ?

Les causes fréquentes sont une erreur de l’application, une configuration incorrecte, une dépendance indisponible, un manque de mémoire ou une politique de redémarrage.

Faut-il supprimer le conteneur et le recréer ?

Pas systématiquement. La suppression ne corrige pas une variable manquante ou une erreur dans le code. Commencez par examiner les journaux et l’état du conteneur.

Est-ce que docker restart résout le problème ?

Cette commande peut relancer temporairement un service, mais elle ne corrige pas une erreur persistante. Si le processus quitte immédiatement, le problème réapparaîtra.

Pourquoi les journaux Docker sont-ils vides ?

L’application peut ne rien écrire sur la sortie standard, ou quitter avant de produire un message. Examinez également l’état du conteneur, son code de sortie, sa configuration et les journaux de l’hôte.

Conclusion

Pour résoudre un conteneur Docker qui redémarre en boucle, commencez par docker ps -a et docker logs. Examinez ensuite le code de sortie, les variables d’environnement, les volumes, les dépendances, les ressources et la politique de redémarrage.

Une démarche progressive permet d’identifier l’origine du problème tout en limitant le risque de perte de données ou d’interruption supplémentaire.