Les conteneurs orphelins surgissent comme des bagages non réclamés à l’aéroport : discrètement, obstinément, et toujours quand vous êtes déjà en retard.
Le disque se remplit. Les ports se chevauchent. La supervision hurle à propos de conteneurs « inconnus ». Et le pire : la moitié du temps, « l’orphelin » fait en réalité quelque chose d’utile.
Voici le guide sur le terrain pour déterminer ce qui est vraiment orphelin, ce qui est juste mal étiqueté, et comment nettoyer sans transformer une fenêtre de maintenance en événement de carrière.
Ce que « conteneur orphelin » signifie réellement (et ce que ce n’est pas)
« Conteneur orphelin » n’est pas un concept du runtime Docker. Docker n’a pas de drapeau qui dit « ce conteneur est orphelin ».
« Orphelin » est une étiquette opérationnelle qu’on colle aux conteneurs qui semblent découplés des outils et de l’intention qui les ont créés.
En pratique, on qualifie un conteneur d’« orphelin » lorsqu’une (ou plusieurs) des situations suivantes est vraie :
- L’outil de déploiement ne le connaît plus (projet Compose renommé, stack Swarm supprimée, pipeline CI déplacé, etc.).
- Personne ne peut nommer un propriétaire (pas d’étiquettes, pas de mapping de service, pas de ticket, pas de runbook).
- Il tourne toujours, mais personne ne le surveille (pas de métriques, pas d’envoi de logs, pas d’alertes).
- Il est arrêté mais pas supprimé et s’accumule lentement avec d’anciennes versions.
L’idée dangereuse est de croire que « orphelin » équivaut à « sûr à supprimer ». Parfois c’est vrai. Parfois c’est votre seule copie d’un sidecar stateful
que quelqu’un a démarré à la main à 2 h du matin pendant une panne et qu’il a oublié de documenter.
Si vous voulez une définition plus fiable en production : un orphelin est un conteneur dont le contrôleur de cycle de vie a disparu.
Pas de contrôleur signifie pas de mises à jour prévisibles, pas de gestion du dérive, et pas de nettoyage automatisé. C’est pourquoi ils s’accumulent.
Pourquoi des conteneurs orphelins apparaissent dans les systèmes réels
Les orphelins ne sont pas un manque moral. Ce sont une propriété émergente d’équipes qui livrent du logiciel plus vite qu’elles n’implantent l’hygiène opérationnelle.
Voici les principales causes que je vois régulièrement.
1) Dérive du projet/nom Docker Compose
Compose identifie « ses » conteneurs par un nom de projet. Changez le nom du répertoire, utilisez -p parfois mais pas toujours,
ou mettez à jour des versions de Compose avec des valeurs par défaut légèrement différentes, et soudain vous avez plusieurs « projets » sur le même hôte. Les conteneurs de chaque projet
se comportent comme des étrangers pour les autres.
2) La stratégie CI/CD laisse des conteneurs anciens derrière elle
Blue/green ou canary peuvent être propres. Ou ça peut être « démarrer de nouveaux conteneurs, oublier d’arrêter les anciens ».
Surtout avec des scripts ad hoc qui font docker run avec de nouveaux tags d’image et ne suppriment jamais le conteneur précédent.
3) Les boucles de crash créent des cimetières de conteneurs arrêtés
Un conteneur qui échoue immédiatement peut être relancé par une politique ou un superviseur. Mais les anciennes instances peuvent rester si elles sont créées à répétition
par un pipeline, un script, ou des runs Compose avec des noms changeants. Vous vous retrouvez avec des dizaines ou des centaines de conteneurs exited, chacun avec des logs
et des données de couche inscriptible consommant du disque.
4) Les ingénieurs lancent « juste un conteneur rapide » en prod
Vous reconnaissez la forme de l’incident : quelqu’un a besoin d’une migration ponctuelle, d’un outil de debug, d’une capture de paquets, d’une sauvegarde. Il lance un conteneur.
Il jure qu’il le supprimera. Puis Slack arrive. Puis le quatrième trimestre arrive. Maintenant c’est immortel.
5) Swarm, Kubernetes ou systemd ont changé, mais les conteneurs sont restés
Quand vous migrez de couches d’orchestration, il y a une période transitoire où d’anciens hôtes exécutent encore des workloads legacy.
Supprimez l’orchestrateur et vous supprimez la partie qui faisait le nettoyage.
6) Les réseaux et volumes survivent aux conteneurs (par conception)
Docker traite intentionnellement les volumes comme durables. C’est bien. Mais cela signifie aussi que le nettoyage n’est pas « supprimer les conteneurs » — c’est « supprimer conteneurs,
réseaux, volumes et images avec le bon ensemble de contraintes ». En manquez un et vous continuez à saigner du disque.
Une petite blague, parce que vous l’avez méritée : le nettoyage Docker ressemble à désencombrer un garage — tout est « temporaire » jusqu’à ce que ce soit une pile porteuse.
Faits intéressants et contexte historique (ce qui explique le bazar actuel)
- Les volumes Docker ont été conçus pour survivre aux conteneurs, ce qui explique pourquoi supprimer les conteneurs ne libère pas forcément votre espace disque « réel ».
- Docker Compose ciblait à l’origine le développement local. Son usage en production est devenu courant parce que c’était pratique, pas parce que c’était parfait pour l’ops.
- Les workflows Docker précoces utilisaient des « pets » (conteneurs gérés manuellement) bien avant que l’infrastructure immuable ne devienne la norme.
- Les labels Compose sont devenus le système de métadonnées de facto : Compose moderne marque les conteneurs avec des labels comme
com.docker.compose.projectetcom.docker.compose.service. - Les « images dangling » sont un effet secondaire des builds en couches : quand un tag bouge, les anciennes couches peuvent rester référencées par rien mais occuper de l’espace.
- Le comportement du système de fichiers overlay de Docker compte : les couches inscriptibles des conteneurs peuvent grossir même si votre appli écrit des données « temporaires », parce que « temporaire » peut être à l’intérieur de la couche.
- Les politiques de redémarrage peuvent masquer des échecs : un conteneur qui redémarre sans cesse paraît « en bonne santé » dans
docker pssi vous ne regardez que le temps « Up ». - Compose v2 est passé dans l’interface CLI Docker (sous
docker compose), ce qui a modifié les chemins d’installation et parfois le comportement à travers les flottes. - Les commandes prune ont été ajoutées parce que les gens remplissaient constamment les disques. Elles sont puissantes, tranchantes, et faciles à mal utiliser.
Playbook de diagnostic rapide
Quand vous suspectez des conteneurs orphelins, vous répondez généralement à une des trois douleurs : pression disque, conflits de ports, ou « c’est quoi ce truc » pendant un incident.
Ce playbook vous amène rapidement au goulot sans supprimer la mauvaise chose.
Première étape : identifier la catégorie de douleur en 60 secondes
- Pression disque : le système de fichiers root de l’hôte Docker se remplit, ou
/var/lib/dockerest énorme. - Conflit de ports : le déploiement échoue parce qu’un port est déjà lié, ou le trafic va vers le mauvais conteneur.
- Runtime inconnu : la supervision montre des pics CPU/mémoire d’un conteneur que personne ne reconnaît.
Deuxième étape : mapper les conteneurs au « contrôleur »
Votre objectif : déterminer si chaque conteneur suspect est contrôlé par Compose, Swarm, Kubernetes, systemd, ou « l’historique de shell de quelqu’un ».
Les labels et les motifs de nommage vous disent la plupart de ce que vous devez savoir.
Troisième étape : décider « stop » versus « remove » versus « leave »
Arrêter est réversible-ish (jusqu’à ce que vous oubliez pourquoi il existait). Supprimer est définitif pour la couche inscriptible, pas forcément pour les volumes.
Laisser est acceptable quand vous ne pouvez pas prouver que c’est sûr et que vous avez besoin de temps pour enquêter.
Quatrième étape : nettoyer la bonne classe de ressources
Les conteneurs ne sont pas la seule fuite. Images, cache de build, volumes et réseaux ont chacun leurs modes de défaillance.
Réparez le plus gros coupable d’abord, et ne touchez pas aux volumes à moins d’avoir vérifié qu’ils sont inutilisés et ne font pas partie d’un plan de restauration.
Une citation fiabilité qui tient la route : « L’espoir n’est pas une stratégie. » — idée paraphrasée souvent attribuée aux ingénieurs et opérateurs du monde entier.
Le point est simple : ne comptez pas sur « probablement sûr à supprimer » ; vérifiez.
Tâches pratiques : commandes, sorties et décisions (12+)
Voilà les commandes que j’exécute sur de vrais hôtes. Chaque tâche inclut : la commande, ce que signifie la sortie, et la décision que vous prenez ensuite.
Exécutez-les avec un utilisateur ayant les privilèges Docker (souvent root ou dans le groupe docker). En production, préférez le faire dans une session screen/tmux
et conservez un journal de vos actions.
Task 1: List running containers with high-signal columns
cr0x@server:~$ docker ps --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
CONTAINER ID NAMES IMAGE STATUS PORTS
8c1f2a9d1b33 billing-api-1 billing:2026.01.03 Up 3 days 0.0.0.0:8080->8080/tcp
2b7d9c0e6c12 tmp_migrate_2025_11 alpine:3.19 Up 90 days 0.0.0.0:9000->9000/tcp
Signification : Vous recherchez « ce qui est encore vivant » et « ce qui lie des ports ». Des noms du type tmp_* sont un signe d’alerte, pas une preuve.
Décision : Pour tout élément suspect, passez à l’inspection : labels, montages et ligne de commande.
Task 2: List stopped containers that quietly eat disk
cr0x@server:~$ docker ps -a --filter status=exited --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}'
CONTAINER ID NAMES IMAGE STATUS
a9d0c2f8e1aa billing-api-1_old billing:2025.12.10 Exited (137) 2 weeks ago
f1b2a3c4d5e6 debug-shell ubuntu:22.04 Exited (0) 4 months ago
Signification : Les conteneurs exited conservent leur couche inscriptible et leurs logs jusqu’à suppression.
Décision : S’ils sont clairement supersedés et n’ont pas de volumes importants, supprimez-les. Mais d’abord, vérifiez les montages.
Task 3: Inspect a suspicious container for labels (ownership)
cr0x@server:~$ docker inspect 2b7d9c0e6c12 --format '{{json .Config.Labels}}'
{"com.docker.compose.project":"billing","com.docker.compose.service":"migrate","com.docker.compose.oneoff":"True"}
Signification : Compose l’a créé comme un one-off. Cela signifie souvent docker compose run ou un job de migration.
Décision : Trouvez le projet Compose sur le disque et voyez si c’était censé être temporaire. Un one-off qui tourne depuis 90 jours est rarement intentionnel.
Task 4: Inspect mounts to see if deletion risks data loss
cr0x@server:~$ docker inspect billing-api-1 --format '{{range .Mounts}}{{.Type}} {{.Name}} {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
volume billing_db_data /var/lib/docker/volumes/billing_db_data/_data -> /var/lib/postgresql/data
bind - /srv/billing/config -> /app/config
Signification : Ce conteneur utilise un volume nommé pour les données de la base. Supprimer le conteneur est acceptable ; supprimer le volume ne l’est pas sauf si vous en êtes sûr.
Décision : Vous pouvez supprimer les conteneurs anciens, mais préservez les volumes jusqu’à preuve du contraire ou sauvegarde/plan de restauration en place.
Task 5: Check container size growth (writable layer)
cr0x@server:~$ docker ps -a --size --format 'table {{.Names}}\t{{.Status}}\t{{.Size}}'
NAMES STATUS SIZE
billing-api-1 Up 3 days 12.3MB (virtual 312MB)
tmp_migrate_2025_11 Up 90 days 8.1GB (virtual 18.2GB)
Signification : La couche inscriptible de tmp_migrate_2025_11 est énorme. C’est souvent des logs, caches, ou écritures accidentelles dans le système de fichiers du conteneur.
Décision : Faites un exec pour trouver ce qui grossit, ou capturez les logs et supprimez le conteneur s’il n’est pas censé persister.
Task 6: Identify which containers are binding specific ports
cr0x@server:~$ docker ps --format '{{.Names}} {{.Ports}}' | grep -E '0\.0\.0\.0:8080|:8080->'
billing-api-1 0.0.0.0:8080->8080/tcp
Signification : Le port 8080 est détenu par billing-api-1. Si votre nouveau déploiement ne peut pas démarrer, c’est la cause probable.
Décision : Vérifiez si c’est l’instance actuelle prévue. Sinon, arrêtez-la proprement et redéployez correctement.
Task 7: Get the full startup command to understand intent
cr0x@server:~$ docker inspect tmp_migrate_2025_11 --format 'Entrypoint={{json .Config.Entrypoint}} Cmd={{json .Config.Cmd}}'
Entrypoint=["/bin/sh","-lc"] Cmd=["python manage.py migrate && python manage.py collectstatic --noinput && sleep 9999999"]
Signification : Quelqu’un a enchaîné une migration puis un sleep infini. Classique « conteneur temporaire devenu permanent ».
Décision : Confirmez que les migrations sont terminées et que rien ne dépend de ce conteneur (ex. volumes montés utilisés ailleurs). Ensuite supprimez-le.
Task 8: Check restart policy (containers that come back like bad ideas)
cr0x@server:~$ docker inspect billing-api-1 --format 'RestartPolicy={{.HostConfig.RestartPolicy.Name}}'
RestartPolicy=unless-stopped
Signification : Si vous redémarrez l’hôte, ce conteneur revient automatiquement. Ce n’est pas de l’orchestration ; c’est de la persistance via la politique de redémarrage.
Décision : Si vous voulez qu’il disparaisse, il faut le supprimer (ou régler la politique sur no et l’arrêter).
Task 9: Map containers to Compose projects and spot “foreigners”
cr0x@server:~$ docker ps -a --format '{{.Names}}' | while read n; do docker inspect "$n" --format '{{.Name}} {{index .Config.Labels "com.docker.compose.project"}}' 2>/dev/null; done | sed 's#^/##'
billing-api-1 billing
billing-db-1 billing
tmp_migrate_2025_11 billing
debug-shell <no value>
Signification : debug-shell n’a pas de label de projet Compose. Cela le rend plus difficile à attribuer et plus probable d’avoir été lancé manuellement.
Décision : Pour les conteneurs sans label, tracez via l’image, la commande, les montages et l’heure de création. Ne supprimez pas à l’aveugle.
Task 10: Find “dangling” and unused resources with a dry-run mindset
cr0x@server:~$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 48 12 21.4GB 15.1GB (70%)
Containers 73 9 9.3GB 8.8GB (94%)
Local Volumes 19 7 120.6GB 22.4GB (18%)
Build Cache 62 0 4.1GB 4.1GB
Signification : Les conteneurs sont très récupérables : vous avez probablement beaucoup de conteneurs arrêtés ou des couches inscriptibles gonflées.
Les volumes sont volumineux mais moins récupérables ; les volumes actifs sont en usage.
Décision : Commencez par les conteneurs et les images. Touchez aux volumes seulement après avoir confirmé qu’ils sont inutilisés et qu’ils ne sont pas critiques pour la récupération.
Task 11: Show which volumes are unused (but don’t delete yet)
cr0x@server:~$ docker volume ls --format 'table {{.Name}}\t{{.Driver}}'
NAME DRIVER
billing_db_data local
billing_cache local
old_tmp_data local
cr0x@server:~$ docker volume inspect old_tmp_data --format 'Name={{.Name}} Mountpoint={{.Mountpoint}}'
Name=old_tmp_data Mountpoint=/var/lib/docker/volumes/old_tmp_data/_data
Signification : Lister les volumes ne vous dit pas s’ils sont attachés. L’inspection montre où ils vivent sur le disque.
Décision : Croisez les références depuis les conteneurs avant de supprimer des volumes.
Task 12: Determine if any container still references a volume
cr0x@server:~$ docker ps -a --format '{{.ID}} {{.Names}}' | while read id name; do docker inspect "$id" --format '{{.Name}} {{range .Mounts}}{{.Name}} {{end}}' | sed 's#^/##'; done | grep -w old_tmp_data || true
Signification : Aucune sortie signifie qu’aucun conteneur n’a actuellement old_tmp_data monté.
Décision : C’est un candidat à la suppression, après avoir confirmé qu’il n’est pas destiné à des restaurations futures ou des workflows manuels.
Task 13: Safely stop a container (observe impact)
cr0x@server:~$ docker stop --time 30 tmp_migrate_2025_11
tmp_migrate_2025_11
Signification : Docker a envoyé SIGTERM et a attendu jusqu’à 30 secondes avant SIGKILL. Arrêter est réversible : vous pouvez redémarrer si besoin.
Décision : Surveillez la santé de l’application (requêtes, erreurs, files). Si rien ne change, procédez à la suppression.
Task 14: Remove container and confirm it’s gone
cr0x@server:~$ docker rm tmp_migrate_2025_11
tmp_migrate_2025_11
cr0x@server:~$ docker ps -a --format '{{.Names}}' | grep -w tmp_migrate_2025_11 || echo "not found"
not found
Signification : Le conteneur est supprimé. Les volumes nommés restent à moins d’être explicitement supprimés.
Décision : Relancez docker system df pour vérifier l’espace récupéré et vous assurer que vous n’avez pas supprimé quelque chose d’actif.
Task 15: Remove a Compose project’s orphans the correct way
cr0x@server:~$ cd /srv/billing
cr0x@server:~$ docker compose ps
NAME IMAGE SERVICE STATUS
billing-api-1 billing:2026.01.03 api running
billing-db-1 postgres:15 db running
cr0x@server:~$ docker compose up -d --remove-orphans
[+] Running 2/2
✔ Container billing-db-1 Running
✔ Container billing-api-1 Running
Signification : Compose réconcilie l’état du projet et supprime les conteneurs du même projet qui ne sont pas déclarés dans le fichier Compose actuel.
Décision : Utilisez ceci lorsque vous faites confiance au fichier Compose comme source de vérité. Ne l’utilisez pas dans un répertoire dont vous n’êtes pas sûr qu’il corresponde à la production.
Task 16: Controlled pruning (the least-worst way)
cr0x@server:~$ docker image prune -f
Deleted Images:
deleted: sha256:4f9c1a...
Total reclaimed space: 2.3GB
cr0x@server:~$ docker container prune -f
Deleted Containers:
a9d0c2f8e1aa
f1b2a3c4d5e6
Total reclaimed space: 6.7GB
Signification : image prune supprime les images inutilisées (non référencées par aucun conteneur). container prune supprime les conteneurs arrêtés.
Décision : C’est généralement sûr sur des configurations mono-hôte si vous comprenez ce que « inutile » signifie. Ce n’est pas sûr si vous comptez sur des conteneurs arrêtés comme artefacts forensiques.
Orphelins Docker Compose : le suspect habituel
La plupart des discussions sur les « conteneurs orphelins » finissent par parler de Compose, car Compose est excellent pour créer des conteneurs et modérément opiniâtre quant au nettoyage.
Compose suit les ressources via des labels et un nom de projet. Ce nom de projet peut venir de :
- le nom du répertoire d’où vous l’avez exécuté,
- le flag
-p, - le champ
name:dans les specs Compose récentes (selon versions et outils).
Changez l’un de ces éléments et vous pouvez créer un second univers de conteneurs qui semblent sans lien. Du point de vue de Docker, ils ne sont pas « orphelins ».
Ils sont orphelins de votre modèle mental.
Compose crée aussi des conteneurs « one-off » (label com.docker.compose.oneoff=True) quand vous faites des choses comme :
docker compose runpour des migrations,- des tâches d’administration ad hoc,
- des commandes de débogage qui se terminent par un long sleep parce que quelqu’un voulait « le laisser tourner ».
L’approche sûre est de traiter Compose comme une infrastructure déclarative : le fichier est le contrat. Réconciliez-le avec docker compose up -d --remove-orphans.
Ensuite, arrêtez de lancer des one-offs en prod sans plan de nettoyage.
Stratégie de purge sûre (ce que je fais réellement)
Purger en toute sécurité, c’est ordonner et prouver. Vous voulez supprimer les déchets sans supprimer la chose qui garde silencieusement une intégration legacy en vie.
Voici la stratégie qui s’adapte d’un hôte unique à une petite flotte.
Étape 1 : Classer les conteneurs en trois seaux
- Déclarés : créés par un outil connu (projet Compose que vous trouvez, service Swarm, etc.).
- Probablement déclarés : a des labels ou des motifs de nommage suggérant une propriété, mais vous ne trouvez pas immédiatement le contrôleur.
- Non déclarés : pas de labels, pas de repo évident, personne ne peut l’expliquer.
Les conteneurs déclarés sont faciles : corrigez le contrôleur, pas le symptôme. Les « probablement déclarés » demandent un peu d’archéologie. Les non déclarés exigent de la prudence.
Étape 2 : Préférez arrêter avant de supprimer
Arrêter vous donne un levier de rollback. Si quelqu’un crie, vous pouvez redémarrer. Si rien ne casse après une fenêtre d’observation raisonnable, supprimez.
« Raisonnable » dépend du workload : minutes pour des API sans état derrière un load balancer ; heures ou jours pour des tâches cron qui tournent chaque semaine.
Étape 3 : Séparez le nettoyage des conteneurs du nettoyage des volumes
Les conteneurs sont jetables. Les volumes sont là où reposent les corps. Une campagne de nettoyage sûre typiquement :
- supprime les conteneurs exited,
- supprime les images inutilisées et le cache de build,
- évalue ensuite les volumes, un par un, avec des preuves.
Étape 4 : Mettez des garde-fous pour éviter la réapparition d’orphelins
Si vous nettoyez une fois, vous reviendrez. Des garde-fous efficaces :
- Standardiser les noms de projet Compose (
-pou nom explicite) par environnement. - Exiger des labels pour la propriété et les références de ticket sur les conteneurs ad hoc.
- Rapports planifiés : « conteneurs sans labels compose/swarm » est un audit peu coûteux.
- Alertes disque sur
/var/lib/dockeravec assez de marge pour agir avant les incidents.
Deuxième blague, parce que l’univers est injuste : Rien n’est plus permanent qu’un conteneur temporaire démarré avec « Je le supprimerai après le déjeuner ».
Trois mini-histoires du monde de l’entreprise
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
Une entreprise SaaS de taille moyenne exploitait quelques hôtes Docker « simples » pour des services internes — exports de facturation, quelques pipelines ETL, et un dashboard.
Pas d’orchestrateur. Juste Compose et beaucoup de confiance.
Lors d’un déploiement de routine, l’ingénieur d’astreinte a lancé docker system prune -a -f pour libérer de l’espace disque.
Ça a marché. Le disque a récupéré. Le déploiement a continué. Puis les exports clients ont commencé à échouer avec des erreurs d’authentification incompréhensibles.
La mauvaise hypothèse : « Les images inutilisées sont sûres à supprimer. » Dans leur workflow, un conteneur arrêté était conservé comme spare chaud pour un job legacy
qui ne tournait qu’en fin de mois. Ce conteneur référençait une image qui n’était utilisée par aucun conteneur en cours lors du prune.
Lorsque la fin de mois est arrivée, leur automatisation a tenté de démarrer le job instantanément. Récupérer l’image nécessitait l’accès au registre, qui était parfois bloqué
par des changements de pare-feu. Le job n’a pas tourné. La finance a remarqué. Ça a rapidement escaladé.
La correction n’a pas été « ne plus jamais pruner ». C’était de déclarer, prévoir et tester les jobs de fin de mois, et de mettre en cache ou miroir les images nécessaires.
Ils ont aussi appris à traiter l’état « arrêté mais important » comme réel et à le documenter et monitorer.
Mini-histoire 2 : L’optimisation qui s’est retournée contre eux
Une autre organisation voulait des déploiements plus rapides. Quelqu’un a remarqué que Compose recrée les conteneurs quand la configuration change, et que les anciens s’accumulent.
Ils ont « optimisé » en passant à un script qui faisait docker run avec des noms de conteneurs uniques par build, laissant les anciens arrêtés « au cas où ».
Au début, c’était super. Le rollback était « démarrer le conteneur précédent ». Pas de pulls depuis le registre, pas d’attente. L’équipe s’est félicitée.
Puis l’hôte a atteint la pression disque. Pas à cause des images, mais à cause des conteneurs exited et de leurs couches inscriptibles. Chaque build écrivait quelques centaines de mégaoctets de caches
dans le système de fichiers du conteneur. Multipliez par des semaines de déploiements et vous obtenez une défaillance lente.
Le revers fut la complexité opérationnelle : le mécanisme de rollback était manuel et sujet à erreur, et l’histoire du nettoyage est devenue « quelqu’un devrait pruner parfois ».
Quand le disque s’est rempli, Docker a commencé à échouer au démarrage des conteneurs, puis la journalisation a cessé, et finalement même les sessions SSH sont devenues étranges car le rootfs était presque plein.
La solution à long terme était ennuyeuse : utiliser un vrai pattern de déploiement avec rétention explicite (conserver N images précédentes), stocker les caches dans des volumes ou stores externes,
et intégrer le nettoyage au pipeline de déploiement. Ils ont remplacé le « rollback ad hoc » par des rollbacks reproductibles.
Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la situation
Une entreprise liée aux paiements exploitait plusieurs hôtes Docker avec un contrôle des changements strict. Pas glamour, mais efficace. Ils avaient un job hebdomadaire programmé qui collectait :
docker ps -a, docker system df, et la liste des conteneurs sans labels de propriété. La sortie alimentait automatiquement un ticket interne.
Une semaine, le rapport a signalé un nouveau conteneur en cours d’exécution sans labels Compose, liant un port élevé et consommant du CPU en continu.
Ce n’était pas assez lourd pour déclencher des alertes, mais c’était étrange.
L’ingénieur d’astreinte l’a tracé via docker inspect : il montait un répertoire hôte contenant des secrets applicatifs, et il avait été démarré depuis une image de base générique.
Ça ressemblait soit à un raccourci de debug soit à quelque chose de pire. Ils l’ont arrêté dans une fenêtre contrôlée et ont observé les métriques. Rien n’a cassé.
Après enquête interne, il s’est avéré que c’était un conteneur de diagnostic « temporaire » démarré pendant un appel avec un fournisseur. Il avait été laissé en marche et oublié.
La pratique ennuyeuse—le rapport hebdomadaire—a permis de le repérer avant que ça ne devienne un souci de conformité.
Ils n’ont pas sanctionné l’ingénieur qui l’avait lancé. Ils ont changé le processus : les conteneurs ad-hoc exigent des labels et une note d’expiration, et le rapport est devenu une vérification quotidienne sur les hôtes critiques.
Personne n’a écrit d’article de blog à ce sujet. Tout a continué de fonctionner. C’est la victoire.
Erreurs courantes : symptôme → cause racine → correctif
C’est la section à lire quand vous êtes fatigué et que l’hôte vous pagine. Chaque élément est spécifique parce que les conseils génériques causent des pannes surprises.
1) « J’ai supprimé le conteneur mais l’utilisation disque n’a pas changé »
- Symptôme :
docker rms’exécute, mais/var/lib/dockerreste énorme. - Cause racine : Les volumes et images consomment encore de l’espace ; la couche du conteneur n’était pas le principal coupable.
- Correctif : Lancez
docker system df; prunez les conteneurs arrêtés ; prunez les images inutilisées ; examinez les volumes individuellement avant suppression.
2) « docker compose down n’a pas supprimé le conteneur bizarre »
- Symptôme : Le projet Compose est down, mais certains conteneurs restent.
- Cause racine : Le conteneur appartient à un autre nom de projet Compose (dérive de répertoire ou mismatch
-p), ou il n’a pas été créé par Compose. - Correctif : Inspectez le label
com.docker.compose.project. Lancez Compose depuis le bon répertoire avec le bon nom de projet, ou supprimez manuellement après vérification.
3) « Un conteneur revient sans cesse après que je l’arrête »
- Symptôme : Vous l’arrêtez, et il redémarre.
- Cause racine : Politique de redémarrage (
always/unless-stopped) ou un contrôleur externe (systemd, Swarm, Kubernetes) le recrée. - Correctif : Identifiez le contrôleur via les labels. Désactivez le contrôleur ou supprimez la définition du service. Pour la politique de redémarrage, supprimez le conteneur ou recréez-le avec
--restart=no.
4) « Après un prune, une appli ne démarre plus car l’image manque »
- Symptôme : Le démarrage échoue et Docker tente de puller de manière inattendue.
- Cause racine : Vous avez pruné des images non référencées par des conteneurs en cours, mais sur lesquelles vous comptiez pour des démarrages rapides ou jobs planifiés.
- Correctif : Déclarez et testez les jobs ; assurez l’accès au registre ; conservez un cache maîtrisé d’images ; prunez selon une politique, pas une émotion.
5) « Après le nettoyage, l’appli démarre mais les données ont disparu »
- Symptôme : Le service tourne mais la base/le contenu est réinitialisé.
- Cause racine : Vous avez supprimé un volume nommé ou basculé involontairement sur un volume anonyme.
- Correctif : Arrêtez de supprimer les volumes à la légère. Vérifiez les montages avec
docker inspect. Utilisez des volumes nommés explicites. Sauvegardez et testez la restauration.
6) « Il y a des dizaines de conteneurs au nom similaire »
- Symptôme : Conteneurs comme
api_1,api_1_old,api_1_202512, etc. - Cause racine : Les scripts de déploiement créent de nouveaux conteneurs au lieu de recréer ; dérive de projet Compose ; rollbacks manuels.
- Correctif : Standardisez le nommage et les contrôleurs. Utilisez la réconciliation Compose ou un orchestrateur. Définissez la rétention explicitement (N images précédentes), pas N conteneurs précédents.
7) « Les conteneurs orphelins ont des logs énormes »
- Symptôme : Le disque se remplit ; les logs Docker grossissent ; les conteneurs montrent une grande taille.
- Cause racine : Le driver de logs est
json-filesans rotation ; applis bavardes ; boucles de crash. - Correctif : Configurez la rotation des logs au niveau du daemon Docker ou par conteneur ; pensez à une solution de logs centralisée ; supprimez les conteneurs aux logs volumineux après avoir capturé ce dont vous avez besoin.
Listes de contrôle / plan étape par étape
Checklist A: « J’ai besoin de libérer du disque en sécurité aujourd’hui »
- Obtenez une base :
docker system dfet un contrôle du système de fichiers sur/var/lib/docker. - Supprimez d’abord les conteneurs arrêtés :
docker container prune(ou suppression manuelle après revue). - Prunez le cache de build si vous builderez localement :
docker builder prune(vérifiez que vous ne dépendez pas de ce cache pour la perf pendant l’incident). - Prunez les images inutilisées :
docker image prune(évitez-asauf si vous comprenez les jobs planifiés et les besoins de cold-start). - Ce n’est qu’ensuite que vous considérez les volumes : identifiez les volumes inutilisés ; vérifiez qu’ils ne sont pas référencés ; validez la posture backup/restore ; supprimez sélectivement.
- Re-vérifiez :
docker system df. Confirmez que l’alerte s’efface et que Docker peut démarrer de nouveaux conteneurs.
Checklist B: « J’ai trouvé un conteneur inconnu en cours d’exécution »
- Ne le supprimez pas encore. Identifiez-le :
docker ps, puisdocker inspectpour labels et montages. - Vérifiez les ports : s’il bind des ports publics, considérez-le urgent.
- Vérifiez la commande : cherchez des shells de debug, des sleeps, ou des processus de tunnel.
- Vérifiez les montages : les chemins hôte et secrets sont à haut risque.
- Arrêtez-le avec une fenêtre d’observation. Si rien ne casse, supprimez-le et ouvrez un ticket pour prévenir la récurrence.
Checklist C: « Prévenir le retour des orphelins »
- Standardisez le nommage de projet Compose (
-pfixe par env) et conservez les fichiers Compose dans des chemins connus. - Exigez des labels sur
docker runen production (propriétaire, ticket, expiration). - Mettez en place un audit programmé : conteneurs sans labels de propriété ; volumes non référencés ; espace récupérable total.
- Configurez la rotation des logs et surveillez l’usage disque avec suffisamment d’avance pour agir.
- Transformez les one-off en vrais jobs (déclarés, reproductibles, monitorés).
FAQ
1) Qu’est-ce qu’un « conteneur orphelin » en termes Docker ?
Docker ne le définit pas formellement. Les opérateurs utilisent « orphelin » pour désigner « un conteneur toujours présent sur l’hôte sans propriétaire/contrôleur clair ».
2) Les conteneurs orphelins sont-ils toujours sûrs à supprimer ?
Non. « Orphelin » signifie souvent « personne ne se souvient pourquoi il existe », ce qui n’est pas équivalent à « inutile ». Vérifiez les montages, ports et le trafic avant de supprimer.
3) Pourquoi j’ai des avertissements d’orphelins avec Docker Compose ?
Compose avertit quand des conteneurs existent dans le même projet mais ne sont pas définis dans le fichier Compose courant. Cela arrive après des renommages, suppressions de services, ou dérive de nom de projet.
4) Que supprime réellement docker compose up -d --remove-orphans ?
Les conteneurs du même projet Compose qui ne sont pas déclarés dans le fichier Compose actuel. Il ne supprime pas les volumes sauf si vous demandez explicitement à Compose de les enlever.
5) Quelle est la commande « prune » la plus sûre ?
docker container prune est généralement la plus sûre car elle cible uniquement les conteneurs arrêtés. Ensuite vient docker image prune pour les images inutilisées.
Soyez prudent avec docker system prune -a, surtout sur des hôtes exécutant des jobs planifiés ou où les pulls à froid sont risqués.
6) Comment trouver qui a créé un conteneur ?
Commencez par docker inspect et regardez les labels. Compose, Swarm et beaucoup de systèmes CI ajoutent des labels utiles.
Si les labels manquent, inspectez la commande, les montages, le nom de l’image et la date de création, et corrélez avec les logs de déploiement.
7) Si je supprime un conteneur, est-ce que je perds des données ?
Vous perdez la couche inscriptible du conteneur. Les données dans les volumes nommés persistent. Les données écrites dans le système de fichiers du conteneur (pas un volume) sont perdues.
Vérifiez toujours les montages avant de supprimer.
8) Pourquoi les conteneurs exited prennent-ils autant d’espace ?
Les conteneurs exited conservent leur couche filesystem et leurs logs. Une appli bavarde utilisant les logs JSON par défaut peut générer de gros fichiers même quand le conteneur n’est plus en cours d’exécution.
9) Pourquoi des conteneurs réapparaissent-ils après un reboot ?
Des politiques de redémarrage comme unless-stopped peuvent les ramener, et des contrôleurs externes peuvent les recréer. Identifiez le mécanisme applicable avant de conclure que « Docker est hanté ».
10) Comment empêcher les conteneurs orphelins lors de maintenances ponctuelles ?
Préférez exécuter les one-offs comme des jobs déclarés (service Compose, tâche planifiée, ou job orchestré). Si vous devez utiliser docker run, mettez des labels et un plan de nettoyage,
et évitez les sleeps longue durée.
Prochaines étapes que vous pouvez faire aujourd’hui
Si vous ne faites que trois choses, faites celles-ci :
- Lancez
docker system dfet identifiez si ce sont les conteneurs, images, volumes ou le cache de build qui sont les vrais gourmands disque. - Auditez la propriété : listez les conteneurs sans labels Compose/stack et inspectez leurs montages et ports avant d’y toucher.
- Mettez en place une routine récurrente de nettoyage et de reporting, puis standardisez le nommage des projets Compose pour arrêter de créer des univers parallèles.
Les conteneurs orphelins ne sont pas un mystère Docker. Ce sont des échecs de gestion du cycle de vie. Réparez le cycle de vie, et les « orphelins » disparaîtront en grande partie—ainsi que les alertes disque à 2 h du matin.