Docker prune : libérer de l’espace disque sur un VPS
Votre VPS est plein ? Utilisez docker system df pour repérer images, conteneurs, build cache et volumes, puis lancez le prune ciblé sans supprimer vos données.
Déterminez ce qui utilise l’espace disque avant tout nettoyage
Docker utilise l’espace disque d’un VPS à quatre endroits : les images, les conteneurs arrêtés, le build cache et les volumes locaux. Exécutez d’abord docker system df pour déterminer lequel contient les données, puis utilisez le prune le plus ciblé possible. L’ordre est important, car la dernière commande de ce guide, docker volume prune -a, supprime des données sans possibilité d’annulation.
Commencez par le système de fichiers, pas par Docker.
df -h /
sudo du -xh --max-depth=1 /var/lib/docker | sort -hdf indique le niveau d’occupation. du indique où l’espace est utilisé. L’option -x limite du à un seul système de fichiers. Elle l’empêche donc de suivre un montage vers un volume séparé et de le compter deux fois. Cinq répertoires sont importants ici : overlay2 contient les layers des images et des conteneurs, volumes contient les données des volumes, containers contient les métadonnées des conteneurs et les fichiers de log, buildkit contient le build cache et image contient les métadonnées des layers.
Un point sur sudo et les wildcards du shell, car cela fait perdre beaucoup de temps. /var/lib/docker appartient à root et n’est pas lisible par votre utilisateur normal. ls /var/lib/docker renvoie donc Permission denied. Une commande comme sudo du -sh /var/lib/docker/* échoue également, car votre shell développe * avant l’exécution de sudo, et votre shell ne peut pas lire ce répertoire. Toutes les commandes ci-dessous utilisent find ou --max-depth à la place d’une wildcard, précisément pour cette raison.
Voyons maintenant la vue de Docker.
docker system dfTYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 24 6 12.4GB 8.91GB (71%)
Containers 31 6 1.42GB 1.39GB (97%)
Local Volumes 14 5 6.03GB 4.11GB (68%)
Build Cache 212 0 9.87GB 9.87GBCes chiffres proviennent d’une machine et ne disent rien de la vôtre. Examinez plutôt leur répartition. TOTAL compte les objets, ACTIVE compte ceux qui sont actuellement utilisés et RECLAIMABLE correspond à l’estimation de Docker de ce qu’un prune pourrait libérer sur cette ligne.
Deux points concernant RECLAIMABLE posent souvent problème. Les layers d’image partagés sont comptés une fois pour chaque image qui les utilise. La ligne des images indique donc généralement davantage d’espace que ce que vous pourrez réellement libérer. De plus, cette commande n’inclut jamais les fichiers de log des conteneurs, car Docker ne considère pas un fichier de log comme un objet récupérable. Lorsque du indique un répertoire beaucoup plus volumineux que ne le laisse penser docker system df, les fichiers de log en sont la cause. Une section ci-dessous leur est consacrée.
Ajoutez -v pour obtenir le détail par objet.
docker system df -vLe récapitulatif est alors réparti en une section par type d’objet. La section des images ajoute les colonnes SHARED SIZE et UNIQUE SIZE, afin de montrer le coût réel d’une image donnée. La section des volumes ajoute une colonne LINKS, qui indique le nombre de conteneurs attachés à ce volume. Retenez LINKS, car la valeur 0 constitue l’unique critère utilisé par les commandes de prune des volumes.
Images dangling et images inutilisées
Ces deux termes semblent interchangeables, mais ils ne le sont pas. Les filtres se comportent différemment, car ils ciblent des objets différents.
Une image dangling est une image sans tag. Elle apparaît comme <none> dans docker images. Vous en créez une lors de chaque rebuild : docker build -t myapp:latest . déplace le tag myapp:latest vers la nouvelle image, tandis que l’ancienne conserve toutes ses layers, mais perd son nom. Plus rien ne la référence et aucun mécanisme ne la supprime automatiquement.
Une image inutilisée est une image, taguée ou non, à laquelle aucun container ne fait actuellement référence. Un postgres:16 que vous avez pull le mois dernier et que vous n’exécutez pas actuellement est inutilisé, mais il n’est pas dangling.
docker image prune # dangling images only
docker image prune -a # every image no container refers toLa deuxième commande commence par poser une question.
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N]Lisez attentivement cette invite. « Associés à ces images » désigne un objet container existant, en cours d’exécution ou arrêté. Si vous avez exécuté docker compose down, les containers ont été supprimés. Toutes les images utilisées par ces services sont donc désormais inutilisées, et -a les supprime toutes. Vous ne perdez rien que vous ne puissiez récupérer, mais le prochain docker compose up -d devra à nouveau pull ou rebuild l’ensemble. Sur un petit VPS, cela consomme de la bande passante et du temps de build. C’est une bonne raison de savoir ce que docker compose down supprime et ce que stop laisse en cours d’exécution avant de lancer un prune.
Un filtre permet d’exclure les images récentes.
docker image prune -a --filter "until=240h"Cette commande supprime les images inutilisées créées il y a plus de 240 heures (10 jours) et conserve les plus récentes. La valeur until accepte une chaîne de durée Go telle que 240h, ou un timestamp absolu comme 2026-08-01T00:00:00.
Comprendre le cache de build et les raisons de sa croissance illimitée
BuildKit est le builder utilisé par défaut par Docker pour docker build et docker compose build depuis Docker Engine 23.0. Il met en cache le résultat de chaque étape de chaque Dockerfile qu’il exécute et conserve ce cache dans /var/lib/docker/buildkit. Ce cache permet à votre deuxième build de se terminer en quelques secondes. Il remplit donc son rôle. Le problème est qu’aucune expiration des anciennes entrées n’est configurée par défaut. Si vous construisez cinquante fois la même image avec une étape COPY qui change à chaque fois, vous conservez cinquante ensembles de layers.
Le build cache est invisible pour docker image prune. Il s’agit d’un type d’objet distinct, qui possède sa propre commande.
docker builder prune # dangling cache
docker builder prune -a # all unused cache
docker builder prune --filter until=168h # cache untouched for 7 daysAucune de ces commandes ne modifie vos images ni vos données. Le seul coût de la suppression du build cache est que le build suivant sera lent une seule fois. Sur un VPS qui reconstruit régulièrement des images, Build Cache est souvent la ligne la plus volumineuse dans docker system df. C’est donc le gros élément que vous pouvez supprimer avec le moins de risques.
Commandes de nettoyage, de la plus sûre à la plus destructive
Parcourez cette liste dans l’ordre et arrêtez-vous dès que df -h / semble de nouveau fonctionner correctement. Chaque commande affiche une ligne Total reclaimed space: à la fin de son exécution.
docker container prunesupprime les conteneurs arrêtés. Leurs couches inscriptibles sont également supprimées. Tout ce qu’un conteneur a écrit en dehors d’un volume est donc supprimé avec lui. Les volumes ne sont pas concernés.docker image prunesupprime uniquement les images orphelines. C’est la commande de suppression d’images la plus sûre.docker builder prunesupprime le build cache orphelin. La contrepartie est un build plus lent.docker image prune -asupprime toutes les images référencées par aucun conteneur. Il faudra ensuite les télécharger de nouveau ou les reconstruire.docker system pruneexécute les trois premières opérations en une seule fois et ajoute les réseaux inutilisés.docker volume prunesupprime les volumes anonymes inutilisés.docker volume prune -asupprime les volumes inutilisés, y compris les volumes nommés. C’est la commande qui supprime les bases de données.
docker system prune indique son propre périmètre avant de s’exécuter.
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- unused build cache
Are you sure you want to continue? [y/N]Les volumes sont volontairement absents de cette liste. Ajouter --volumes inclut de nouveau les volumes anonymes dans le périmètre. Ajouter -a élargit l’étape de suppression des images : elle porte alors sur toutes les images inutilisées, et plus seulement sur les images orphelines. Exécuter un docker system prune -a --volumes -f complet sur un hôte de production est une manière de perdre des données en essayant de libérer de l’espace disque.
Pourquoi le pruning des volumes supprime votre base de données
Cette section mérite d’être lue deux fois.
Un volume est considéré comme inutilisé lorsqu’aucun conteneur n’y est attaché. C’est le seul critère. Docker ne vérifie pas si le volume est vide, si un fichier compose le déclare encore ou s’il contient l’unique copie de votre base de données. LINKS 0 dans docker system df -v signifie qu’il peut être supprimé par le pruning, et rien de plus.
Enchaînez maintenant deux opérations courantes. Vous exécutez docker compose down pour redémarrer proprement une stack. Cette commande supprime les conteneurs et conserve les volumes nommés, conformément à sa documentation. Votre volume Postgres n’est alors plus attaché à aucun conteneur. Dix minutes plus tard, vous exécutez docker volume prune -a pour libérer de l’espace, et la base de données disparaît. Les deux commandes ont fonctionné correctement. C’est leur enchaînement qui a détruit les données.
Depuis Docker Engine 23.0 (API version 1.42), la commande sans option est plus restrictive qu’auparavant.
WARNING! This will remove anonymous local volumes not used by at least one container.Un volume anonyme est un volume créé automatiquement par Docker, généralement parce qu’une image déclare VOLUME et que vous ne lui avez pas attribué de nom. Ces volumes contiennent normalement des données que vous n’aviez pas prévu de conserver. Un volume nommé, comme celui que vous avez indiqué dans votre fichier compose, n’est supprimé que si vous ajoutez -a. Les anciennes versions de Docker supprimaient les deux types avec la commande sans option. Ne vous fiez donc pas aux habitudes prises sur une machine que vous avez depuis mise à niveau. Cette différence n’est compréhensible qu’après avoir vu en quoi les volumes nommés diffèrent des bind mounts, car un bind mount n’est pas un volume Docker et aucune commande de prune ne le touchera.
Vérifiez avant de supprimer. Remplacez myapp_pgdata par le nom du volume que vous contrôlez.
docker volume ls -f dangling=true
docker volume inspect myapp_pgdata
sudo ls -la /var/lib/docker/volumes/myapp_pgdata/_dataLe filtre dangling=true appliqué à un volume signifie qu’il n’est référencé par aucun conteneur, pas qu’il est vide. La commande _data vous montre ce qu’il contient réellement. Si vous y trouvez un répertoire pgdata ou mysql, arrêtez-vous et faites une copie avant d’aller plus loin. La même destruction peut se produire avec docker compose down -v, qui supprime tous les volumes déclarés par le fichier compose sans vous demander de confirmation.
Un volume est la seule ressource d’un hôte Docker qu’une reconstruction ne peut pas recréer. C’est pourquoi les données des volumes doivent figurer dans une sauvegarde restic exécutée hors du serveur, afin qu’une option mal saisie ne puisse pas les atteindre.
Quand rien ne se supprime : les fichiers de journaux des conteneurs
Vous avez tout supprimé, docker system df n’indique presque aucun espace récupérable et le disque est toujours plein. Vérifiez les journaux.
sudo find /var/lib/docker/containers -name '*-json.log' -exec du -h {} + | sort -h | tail -10Chaque conteneur écrit sa sortie standard et sa sortie d’erreur standard dans un fichier JSON situé sous /var/lib/docker/containers/. Dans une installation par défaut, max-size n’est pas défini, ce qui signifie qu’il n’y a aucune limite. Un seul conteneur bloqué dans une boucle de crash peut donc écrire jusqu’à saturer la partition. Aucune commande prune ne supprime ces fichiers, car les conteneurs qui les produisent sont en cours d’exécution et ne peuvent donc, par définition, pas être supprimés par prune.
Ne supprimez pas le fichier. Exécuter rm sur un fichier de journal ouvert ne libère aucun espace, car le daemon Docker conserve un descripteur de fichier ouvert et le kernel maintient ces blocs alloués jusqu’à la fermeture de ce descripteur. df ne bougera pas. Tronquez plutôt le fichier. Cela conserve le même inode et permet au daemon de continuer à écrire.
sudo find /var/lib/docker/containers -name '*-json.log' -exec truncate -s 0 {} +
df -h /Cette solution est temporaire. docker logs pour ces conteneurs ne renvoie désormais rien et les fichiers recommencent immédiatement à grossir. La vraie solution consiste à mettre en place une rotation, décrite dans la section suivante.
Mesurez avant et après, à chaque fois
Ne devinez jamais ce qu’une opération de purge a supprimé. Relevez une valeur, exécutez une commande, puis relevez une nouvelle valeur.
df -h /
docker system df
docker builder prune -f --filter until=168h
docker image prune -f
docker system df
df -h /Comparez les deux sorties df. C’est le seul nombre qui permet de déterminer si votre serveur continue à fournir son service. docker system df indique ensuite quelle ligne a réellement changé, et chaque purge affiche sa propre valeur Total reclaimed space:.
Si df n’a pas changé, mais que docker system df indique que de l’espace a été libéré, un descripteur de fichier ouvert retient des blocs supprimés. C’est le problème lié au fichier journal décrit plus haut. Si les deux valeurs ont changé et que le disque est de nouveau plein dans la journée, vous avez un problème de croissance, pas de nettoyage. La solution consiste à configurer la rotation et une tâche planifiée.
Comment éviter que le disque se remplisse à nouveau
Limitez la taille des journaux. Créez ou modifiez /etc/docker/daemon.json.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Cette configuration limite chaque conteneur à 30 MB de journaux. Chaque valeur sous log-opts doit être une chaîne, y compris les valeurs numériques. Vérifiez que le fichier est correctement analysé avant de redémarrer, car un daemon.json mal formé empêche complètement le daemon de démarrer et arrête tous les conteneurs avec lui.
python3 -m json.tool /etc/docker/daemon.json
sudo systemctl restart docker
docker info | grep -i 'logging driver'docker info doit maintenant renvoyer Logging Driver: json-file. Les limites apparaissent dans la section LogConfig de docker inspect pour un conteneur créé après le redémarrage. C’est le point important : ce paramètre s’applique uniquement aux nouveaux conteneurs. Les conteneurs existants conservent la configuration avec laquelle ils ont été créés. Vous devez donc les recréer.
docker compose up -d --force-recreateLa même limite peut être définie pour chaque service dans un fichier compose. C’est préférable lorsqu’un service particulièrement verbeux nécessite une valeur différente.
services:
app:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"Planifiez un prune ciblé. Exécutez-le chaque semaine, en le limitant aux images dangling et à l’ancien build cache. Ne placez jamais -a ni --volumes dans une tâche planifiée, car une tâche exécutée alors qu’une stack est arrêtée supprimerait les images de cette stack. Avec --volumes, elle commencerait à agir sur vos données.
sudo tee /etc/cron.weekly/docker-prune >/dev/null <<'EOF'
#!/bin/sh
docker image prune -f
docker builder prune -f --filter until=168h
EOF
sudo chmod +x /etc/cron.weekly/docker-prune
sudo /etc/cron.weekly/docker-pruneLa dernière ligne exécute le script une fois manuellement. Vous pouvez ainsi vérifier sa sortie avant toute exécution unattended. Le fichier doit être exécutable et son nom ne doit pas contenir de point, car run-parts ignore les fichiers non exécutables et ceux qui possèdent une extension.
Configurez une alerte sur l’espace libre. Un prune exécuté après le remplissage du disque sert à la récupération. Une alerte à 80 percent sert à la prévention.
usage=$(df --output=pcent / | tr -dc '0-9')
[ "$usage" -ge 80 ] && echo "root filesystem at ${usage} percent full"Ajoutez cette commande à cron avec le notifier que vous utilisez déjà. L’espace libre ne représente qu’une partie du problème. Associez donc cette alerte à la surveillance de l’état du disque sur votre VPS, car un disque défaillant et un disque plein arrêtent tous deux vos conteneurs, mais nécessitent des corrections différentes.
Tout ce qui précède suppose une installation standard avec la racine des données dans /var/lib/docker. Si vous l’avez déplacée avec la clé data-root dans daemon.json, remplacez ce chemin dans chaque commande. Configurer correctement cette organisation sur un serveur neuf fait partie de la configuration de Docker sur un VPS. Il est beaucoup plus simple de prendre cette décision avant d’avoir 40 GB de conteneurs sur la mauvaise partition.
FAQ
Docker system prune supprime-t-il mes volumes ?
Non. La commande simple supprime les conteneurs arrêtés, les réseaux inutilisés, les images dangling et le cache de build inutilisé. Son invite de confirmation liste exactement ces éléments. Les volumes ne sont pris en compte qu’avec --volumes. Depuis Docker Engine 23.0, cette option concerne les volumes anonymes, et non les volumes nommés. Les volumes nommés sont supprimés par docker volume prune -a et docker compose down -v. Ce sont les deux commandes à utiliser avec prudence.
Pourquoi mon disque est-il toujours plein après l’exécution de docker prune ?
Deux causes sont courantes. La première concerne les fichiers de log des conteneurs dans /var/lib/docker/containers/. Aucune commande prune ne les traite. Ils grossissent sans limite tant que vous n’avez pas défini max-size. La seconde concerne un fichier supprimé qu’un processus garde ouvert. Si vous avez supprimé un log avec rm alors que son conteneur était en cours d’exécution, le daemon conserve le descripteur de fichier. Le kernel ne libère donc pas les blocs, et df n’indique aucun changement. Comparez sudo du -xh --max-depth=1 /var/lib/docker et docker system df pour déterminer quel cas vous concerne.
Quelle est la différence entre docker image prune et docker image prune -a ?
La commande simple supprime uniquement les images dangling, c’est-à-dire les images qui ont perdu leur tag, presque toujours à la suite d’un rebuild. La forme -a supprime toutes les images auxquelles aucun conteneur existant ne fait référence, y compris les images taguées que vous avez récupérées volontairement. Après un docker compose down, les conteneurs ont disparu. -a supprimera donc les images de cette stack. Rien n’est définitivement perdu, car le prochain démarrage les récupère ou les rebuild. Toutefois, cette opération peut être longue sur une liaison réseau lente.
Comment empêcher les logs Docker de remplir le disque ?
Définissez max-size et max-file sous log-opts dans /etc/docker/daemon.json, puis redémarrez le daemon avec sudo systemctl restart docker. Ce réglage ne s’applique qu’aux conteneurs créés après ce redémarrage. Vous devez donc recréer les conteneurs en cours d’exécution avec docker compose up -d --force-recreate. Vous pouvez définir les mêmes options pour chaque service dans un fichier compose, sous une clé logging. C’est la solution adaptée lorsqu’un service génère beaucoup plus de logs que les autres.
Peut-on exécuter docker system prune dans une tâche cron sans risque ?
La commande simple docker system prune -f est sûre sur un hôte où toutes les stacks restent actives. Elle supprime toutefois les conteneurs arrêtés. Elle supprimera donc un conteneur que vous avez arrêté volontairement et que vous comptiez redémarrer plus tard. La tâche planifiée plus sûre utilise docker image prune -f et docker builder prune -f --filter until=168h. Elle libère les deux éléments qui grossissent le plus rapidement et ne peut pas toucher aux volumes. Ne planifiez jamais -a ni --volumes.