Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Retrouver des données orphelines sans tout effacer

Par mis à jour le

Améliorer cette page

Un volume qu'aucun conteneur n'utilise est dit orphelin pour Docker, mais cela ne prouve pas qu'il est inutile : il peut porter la base d'un projet que vous avez arrêté ou renommé. Cette leçon vous apprend à retrouver ce volume parmi d'autres, à lire son contenu sans rien risquer, à en garder une copie qui se relit, à le rattacher à un service Compose sans le recréer, puis à supprimer seulement ce qui est jetable. Les mesures viennent d'une VM Debian 13 avec Docker Engine 29.9.2 et Compose 5.6.1.

Chaque geste se vérifie sur l'état réel du moteur, pas sur une supposition.

  • Vous distinguez un volume nommé d'un volume anonyme, et vous savez ce que docker rm, docker rm -v et docker run --rm en font.
  • Vous retrouvez un volume par ses labels, son nom ou son conteneur, et vous lisez son contenu par un conteneur jetable monté en lecture seule.
  • Vous sauvegardez un volume avec tar, puis vous relisez l'archive avant de rien supprimer.
  • Vous rattachez l'ancien volume d'un projet Compose renommé avec external: true, sans le recréer.
  • Vous prévoyez ce que retirent docker volume prune et docker compose down -v, et vous supprimez un volume par son nom seulement.

Vous devez savoir créer un volume nommé, voir la différence avec un volume anonyme et le filtrer avec dangling=true : la leçon Volumes Docker : conserver ses données et monter des fichiers l'explique. Vous devez aussi connaître le nom de projet Compose et l'effet de down -v, présentés dans Persister les données d'une stack recréée. Pour sauvegarder la base d'une stack Compose par un export logique plutôt que par une copie du volume, le guide Sauvegarder et restaurer une application prend le relais.

Jouez les blocs dans l'ordre et dans un même terminal : ils réutilisent la variable IMAGE et le dossier courant. Tous les objets de la démonstration portent le préfixe c41b-m-, ce qui permet de ne cibler qu'eux. Ne jouez pas cette leçon sur un hôte qui porte des volumes à vous : docker volume ls y montrerait aussi vos volumes, et vous risqueriez de les confondre avec ceux de la démonstration.

Fenêtre de terminal
export IMAGE="alpine:3.24.3@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a"
mkdir -p ~/donnees-orphelines/ctx ~/donnees-orphelines/projet
cd ~/donnees-orphelines
docker volume ls
DRIVER VOLUME NAME

La liste est vide sur la VM de mesure. Si la vôtre contient déjà des volumes, notez-les pour vérifier à la fin qu'ils n'ont pas bougé, mais préférez une VM jetable.

Qu'est-ce qu'un volume orphelin, et pourquoi n'est-il pas forcément inutile ?

Section intitulée « Qu'est-ce qu'un volume orphelin, et pourquoi n'est-il pas forcément inutile ? »

Un volume est orphelin quand aucun conteneur ne le référence, même arrêté. Docker le liste avec le filtre dangling=true, qui regroupe les volumes nommés et les volumes anonymes sans distinction. Ce filtre dit seulement qu'aucun conteneur ne l'utilise : il ne dit rien sur la valeur de ce qu'il contient.

Un volume nommé porte le nom que vous lui donnez, par exemple docker volume create c41b-m-data. Un volume anonyme naît sans nom, quand un conteneur déclare un chemin sans source ou quand l'image déclare une instruction VOLUME : Docker lui donne alors un nom de 64 caractères hexadécimaux. Le Dockerfile suivant déclare VOLUME /donnees, après avoir écrit un fichier dans ce dossier.

Fenêtre de terminal
cat > ctx/Dockerfile <<EOF
FROM $IMAGE
RUN mkdir /donnees && echo graine > /donnees/graine.txt
VOLUME /donnees
CMD ["sleep","300"]
EOF
docker build -q -t c41b-m-img:1 ctx
docker run -d --name c41b-m-v1 c41b-m-img:1
ANON=$(docker inspect -f '{{range .Mounts}}{{.Name}}{{end}}' c41b-m-v1)
docker volume ls
sha256:3e5fd844868389b03664723cfcd37d39ecc4aa0f667dde67e8bd4314e260ed9b
fb988cb1a0266f90a6f3b50c5e2dbfed6be3050b600e77a7760f7e6cdcdf8d7b
DRIVER VOLUME NAME
local 7eecf463ce016e35092868234b465ae42e53e9c80d58b32db65843383677c0d6

Dans la dernière sortie, regardez le nom : 64 caractères hexadécimaux, aucun nom lisible. docker run a créé ce volume tout seul, et la variable ANON en garde le nom pour la suite. Ce qui était écrit dans /donnees avant VOLUME est copié dans le volume au premier démarrage : graine.txt s'y retrouve plus bas.

docker volume inspect confirme son origine : le label com.docker.volume.anonymous marque un volume anonyme, et CreatedAt donne sa date de création en UTC.

Fenêtre de terminal
docker volume inspect "$ANON"
[
{
"CreatedAt": "2026-11-04T18:32:38Z",
"Driver": "local",
"Labels": {
"com.docker.volume.anonymous": ""
},
"Mountpoint": "/var/lib/docker/volumes/7eecf463ce016e35092868234b465ae42e53e9c80d58b32db65843383677c0d6/_data",
"Name": "7eecf463ce016e35092868234b465ae42e53e9c80d58b32db65843383677c0d6",
"Options": null,
"Scope": "local"
}
]

Un montage sans nom, comme -v /donnees2, a donné le même résultat : un volume de 64 caractères hexadécimaux. Le guide Volumes Docker montre déjà comment repérer et supprimer un anonyme ; cette leçon s'intéresse à ce qu'on en fait quand on ne sait plus à quoi il servait.

Que devient un volume anonyme quand on supprime son conteneur ?

Section intitulée « Que devient un volume anonyme quand on supprime son conteneur ? »

Supprimer un conteneur avec docker rm laisse son volume anonyme en place. Le volume devient alors orphelin : il apparaît avec dangling=true, et rien ne le supprimera tant que vous ne le demandez pas. Pour retirer le volume avec le conteneur, il faut l'option -v, ou lancer le conteneur avec --rm.

Le tableau ci-dessous résume ce que chaque geste laisse. La démonstration supprime c41b-m-v1 sans -v et vérifie que son volume reste, orphelin : elle sert aussi de base aux sections suivantes.

GesteVolume anonyme du conteneur
docker rm ou docker rm -freste, orphelin
docker rm -v ou docker rm -fvsupprimé avec le conteneur
docker run --rmsupprimé quand le conteneur s'arrête
Fenêtre de terminal
docker rm -f c41b-m-v1
docker volume ls --filter dangling=true
c41b-m-v1
DRIVER VOLUME NAME
local 7eecf463ce016e35092868234b465ae42e53e9c80d58b32db65843383677c0d6

Tant que c41b-m-v1 existait, il référençait son volume, qui n'était donc pas orphelin. Après docker rm -f, le volume apparaît dans la liste, comme le dit la première ligne du tableau. Avec -v ou --rm, le volume part avec le conteneur.

Un conteneur arrêté, et même un conteneur seulement créé sans jamais démarrer, garde son volume référencé. Le test ci-dessous crée un conteneur depuis l'image qui déclare VOLUME, le laisse s'arrêter, puis liste les volumes encore référencés avec dangling=false.

Fenêtre de terminal
docker run --name c41b-m-v3 c41b-m-img:1 true
docker volume ls --filter dangling=false
docker rm -v c41b-m-v3
DRIVER VOLUME NAME
local 6b739925f13f30bf22dfdc4aa3b64984bc6d37d67ae7a95d94284406cbba421f
c41b-m-v3

Le volume de c41b-m-v3 figure sous dangling=false parce que le conteneur arrêté existe encore. Retenez-le : un volume « non monté » peut être encore référencé, et c'est tant mieux, puisque docker volume rm le refuse alors.

Un volume se retrouve par ses labels, par son nom ou par le conteneur qui le référence. Les labels sont le repère le plus direct, mais ils n'existent que si vous les avez posés à la création : un volume sans label ne se distingue que par son nom ou sa date. La démonstration crée donc trois volumes nommés, dont un sans label, et écrit des données dans le premier.

Fenêtre de terminal
docker volume create --label c41b-m-test=1 --label c41b-m-role=donnees c41b-m-data
docker volume create --label c41b-m-test=1 --label c41b-m-role=jetable c41b-m-jetable
docker volume create c41b-m-sanslabel
docker run --rm -v c41b-m-data:/d "$IMAGE" \
sh -c 'echo commande-42 > /d/recu.txt; mkdir /d/sub; echo x > /d/sub/a'
docker volume ls --filter dangling=true
c41b-m-data
c41b-m-jetable
c41b-m-sanslabel
DRIVER VOLUME NAME
local 7eecf463ce016e35092868234b465ae42e53e9c80d58b32db65843383677c0d6
local c41b-m-data
local c41b-m-jetable
local c41b-m-sanslabel

Les quatre volumes sont orphelins, nommés ou non, et la liste ne dit pas lequel porte des données. Le filtre label fait le tri. Une clé seule, ou une paire clé=valeur, fonctionne, et deux filtres label se combinent en ET.

Fenêtre de terminal
docker volume ls --filter dangling=true --filter label=c41b-m-test
docker volume ls --filter label=c41b-m-role=jetable
docker volume ls --filter label=c41b-m-test=1 --filter label=c41b-m-role=donnees
DRIVER VOLUME NAME
local c41b-m-data
local c41b-m-jetable
DRIVER VOLUME NAME
local c41b-m-jetable
DRIVER VOLUME NAME
local c41b-m-data

Le filtre name accepte une sous-chaîne du nom : --filter name=c41b-m- a trouvé les trois volumes nommés. Le gabarit --format affiche les labels sous la forme clé=valeur,clé=valeur. Un volume sans label s'y reconnaît à sa colonne vide.

Fenêtre de terminal
docker volume ls --filter dangling=true --filter name=c41b-m- \
--format '{{.Name}}|{{.Labels}}'
c41b-m-data|c41b-m-role=donnees,c41b-m-test=1
c41b-m-jetable|c41b-m-role=jetable,c41b-m-test=1
c41b-m-sanslabel|

Pour lire une valeur précise, docker volume inspect --format '{{json .Labels}}' rend un objet JSON.

L'autre entrée est le conteneur. docker ps -a --filter volume= liste les conteneurs, arrêtés ou seulement créés, qui référencent un volume ; la sortie est vide quand aucun ne le fait. Pour le voir, créez un conteneur qui monte c41b-m-data sur /lu en lecture seule, sans le démarrer.

Fenêtre de terminal
docker create --name c41b-m-user -v c41b-m-data:/lu:ro "$IMAGE" sleep 300
docker ps -a --filter volume=c41b-m-data --format '{{.Names}} {{.Status}}'
docker ps -a --filter volume=c41b-m-jetable --format '{{.Names}} {{.Status}}'
docker ps -a --filter volume=/lu --format '{{.Names}}'
5939ba135634f49dd570b7b35b2cffb2f1e9bf3a74efc0aefa5405e198d84045
c41b-m-user Created
c41b-m-user

La première commande trouve le conteneur, même au statut Created. La deuxième ne rend rien : aucun conteneur ne référence c41b-m-jetable. La troisième montre que le filtre accepte aussi un point de montage (/lu). Dès que ce conteneur existe, c41b-m-data passe de dangling=true à dangling=false, et docker volume rm refuse de le supprimer.

Fenêtre de terminal
docker volume rm c41b-m-data
Error response from daemon: remove c41b-m-data: volume is in use - [5939ba135634f49dd570b7b35b2cffb2f1e9bf3a74efc0aefa5405e198d84045]

Ce refus est une protection : le moteur ne supprime pas un volume référencé, même par un conteneur qui ne tourne pas. Il dit aussi quel conteneur le garde. Supprimez ce conteneur avant de continuer.

Fenêtre de terminal
docker rm c41b-m-user
c41b-m-user

Comment lire un volume sans l'attacher au service ?

Section intitulée « Comment lire un volume sans l'attacher au service ? »

Pour voir ce qu'un volume contient, montez-le en lecture seule dans un conteneur jetable. Le suffixe :ro interdit toute écriture, donc la lecture ne modifie rien, et --rm supprime le conteneur à la fin. Le volume reste orphelin, puisque le conteneur n'existe plus quand la commande se termine.

Fenêtre de terminal
docker run --rm -v c41b-m-data:/d:ro "$IMAGE" ls -la /d
docker run --rm -v c41b-m-data:/d:ro "$IMAGE" cat /d/recu.txt
total 16
drwxr-xr-x 3 root root 4096 Nov 4 18:32 .
drwxr-xr-x 1 root root 4096 Nov 4 18:32 ..
-rw-r--r-- 1 root root 12 Nov 4 18:32 recu.txt
drwxr-xr-x 2 root root 4096 Nov 4 18:32 sub
commande-42

Regardez la taille de recu.txt : 12 octets, soit commande-42 et son retour à la ligne. La même lecture vérifie que la protection fonctionne : une écriture tentée échoue avec le code 1.

Fenêtre de terminal
docker run --rm -v c41b-m-data:/d:ro "$IMAGE" \
sh -c 'echo piege > /d/ecriture.txt'
sh: can't create /d/ecriture.txt: Read-only file system

La forme longue --mount type=volume,src=c41b-m-data,dst=/d,readonly donne le même résultat, et la lecture marche aussi sur un volume anonyme désigné par son nom de 64 caractères hexadécimaux. Voici le volume anonyme du début, que ANON désigne depuis la création du conteneur : la première commande lit graine.txt, copié depuis l'image, la seconde prouve qu'aucun conteneur ne le référence. Relever ce nom pendant que le conteneur existait évite de chercher un volume parmi tous les anonymes orphelins de l'hôte, dont certains ne sont pas à vous.

Fenêtre de terminal
docker run --rm -v "$ANON":/d:ro "$IMAGE" cat /d/graine.txt
docker ps -a --filter volume="$ANON"
graine
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

La commande docker ps -a n'affiche que son en-tête : aucun conteneur ne référence ce volume. Vous savez maintenant ce que contient chaque candidat, ce qui évite de supprimer un volume sur sa seule mine.

Comment sauvegarder un volume avant de le supprimer ?

Section intitulée « Comment sauvegarder un volume avant de le supprimer ? »

Une sauvegarde se fait avec un conteneur jetable qui monte le volume en lecture seule, et un second montage, en écriture, pour l'archive. L'archive doit vivre hors du volume que vous allez peut-être supprimer, sinon elle disparaît avec lui. Ici, un dossier de l'hôte joue ce rôle.

Fenêtre de terminal
mkdir -p /tmp/c41b-m-bak
docker run --rm -v c41b-m-data:/source:ro -v /tmp/c41b-m-bak:/backup \
"$IMAGE" tar -czf /backup/data.tgz -C /source .
ls -l /tmp/c41b-m-bak
total 4
-rw-r--r-- 1 root root 184 Nov 4 18:32 data.tgz

L'archive pèse 184 octets et appartient à root, puisque le conteneur écrit en root. Une archive qui n'a pas été relue ne prouve rien. Deux relectures complémentaires la vérifient : la liste de son contenu, qui montre que les fichiers y sont, et le contenu d'un fichier précis, qui montre qu'il est lisible.

Fenêtre de terminal
docker run --rm -v /tmp/c41b-m-bak:/backup:ro "$IMAGE" tar -tzf /backup/data.tgz
docker run --rm -v /tmp/c41b-m-bak:/backup:ro "$IMAGE" \
tar -xzOf /backup/data.tgz ./recu.txt
./
./recu.txt
./sub/
./sub/a
commande-42

Le contenu de ./recu.txt est bien commande-42. Pour détecter une altération ultérieure, relevez l'empreinte de l'archive puis contrôlez-la avec sha256sum -c : le contrôle répond OK tant que le fichier est intact.

Fenêtre de terminal
cd /tmp/c41b-m-bak && sha256sum data.tgz > data.tgz.sha256 && cat data.tgz.sha256 && sha256sum -c data.tgz.sha256
cd ~/donnees-orphelines
6f951f3499084cd184ef95b023f5b12a1544aed2d4d52b24a0d6195f333a9577 data.tgz
data.tgz: OK

Une archive tronquée se détecte aussi. Coupez une copie à 50 octets, puis lancez la même lecture : le tar d'Alpine répond avec le code 1.

Fenêtre de terminal
head -c 50 /tmp/c41b-m-bak/data.tgz > /tmp/c41b-m-bak/cass.tgz
docker run --rm -v /tmp/c41b-m-bak:/backup:ro "$IMAGE" tar -tzf /backup/cass.tgz
tar: unexpected end of file
tar: short read

Le message unexpected end of file signale une archive coupée : ne supprimez jamais un volume dont la seule copie donne cette réponse. Un dernier essai prouve que l'archive sert : extraite dans un volume neuf, elle redonne le fichier d'origine.

Fenêtre de terminal
docker volume create --label c41b-m-test=1 c41b-m-restore
docker run --rm -v c41b-m-restore:/d -v /tmp/c41b-m-bak:/backup:ro \
"$IMAGE" tar -xzf /backup/data.tgz -C /d
docker run --rm -v c41b-m-restore:/d:ro "$IMAGE" cat /d/recu.txt
c41b-m-restore
commande-42

Cette copie convient à un volume que personne n'écrit. Pour une base de données en marche, utilisez l'export logique que décrit Sauvegarder et restaurer une application.

Que devient le volume quand un projet Compose change de nom ?

Section intitulée « Que devient le volume quand un projet Compose change de nom ? »

Compose nomme ses volumes projet_volume : changer le nom du projet change le nom du volume, donc Compose en crée un nouveau, vide, et l'ancien devient orphelin. La démonstration le reproduit avec un service Alpine qui garde un fichier dans son volume : une vraie base PostgreSQL n'est pas nécessaire pour voir où vont les données.

Le schéma suit le même parcours que les commandes qui viennent après. Regardez d'abord la branche du projet c41b-m-app2, qui crée un volume vide, puis celle du projet c41b-m-app3, qui retrouve l'ancien volume sans le recréer.

Le projet c41b-m-app crée le volume c41b-m-app_dbdata, qui porte des données. Après compose down, le volume est orphelin. Un projet c41b-m-app2 crée un volume c41b-m-app2_dbdata vide. L'ancien volume se retrouve par le label com.docker.compose.project=c41b-m-app, et un projet c41b-m-app3 le rattache avec external: true, même volume et même date de création.autre nom de projetrattaché sans recréationProjet c41b-m-appcompose upVolume c41b-m-app_dbdatadonnées : ligne-anciennecompose downle conteneur disparaîtVolume orphelindangling=trueProjet c41b-m-app2compose upVolume c41b-m-app2_dbdatavideRetrouvé par le labelcom.docker.compose.project=c41b-m-appProjet c41b-m-app3external: truemême volume, même date
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Enregistrez ce fichier sous ~/donnees-orphelines/projet/compose.yaml. Le volume porte un label à vous, pour retrouver la démonstration au nettoyage. La commande exec écrit un fichier dans le volume, puis down retire le conteneur.

services:
db:
image: ${IMAGE}
command: ["sleep","600"]
volumes:
- dbdata:/var/lib/data
volumes:
dbdata:
labels:
c41b-m-test: "1"
Fenêtre de terminal
cd ~/donnees-orphelines/projet
docker compose -p c41b-m-app up -d
docker compose -p c41b-m-app exec db sh -c 'echo "ligne-ancienne" > /var/lib/data/etat.txt'
docker compose -p c41b-m-app down
docker volume ls --filter name=c41b-m-app --filter dangling=true
Network c41b-m-app_default Creating
[...]
Container c41b-m-app-db-1 Started
Container c41b-m-app-db-1 Stopping
[...]
Container c41b-m-app-db-1 Removed
Network c41b-m-app_default Removed
DRIVER VOLUME NAME
local c41b-m-app_dbdata

Le volume est resté après down, et il est orphelin. Relancer maintenant le même fichier sous un autre nom de projet, avec l'option -p, crée un volume qui n'a pas les données.

Fenêtre de terminal
docker compose -p c41b-m-app2 up -d
docker compose -p c41b-m-app2 exec db cat /var/lib/data/etat.txt
docker volume ls --filter name=c41b-m-app
cat: can't open '/var/lib/data/etat.txt': No such file or directory
DRIVER VOLUME NAME
local c41b-m-app2_dbdata
local c41b-m-app_dbdata

Le fichier est introuvable dans le nouveau volume : c'est le symptôme classique d'un projet renommé, « mes données ont disparu ». Elles n'ont pas disparu, elles sont dans l'ancien volume. Les labels com.docker.compose.* posés par Compose le désignent : com.docker.compose.project porte le nom de l'ancien projet, et com.docker.compose.volume le nom du volume dans le fichier.

Fenêtre de terminal
docker volume ls --filter dangling=true --filter label=com.docker.compose.project=c41b-m-app \
--format '{{.Name}} {{.Labels}}'
docker ps -a --filter volume=c41b-m-app_dbdata --format '{{.Names}} {{.Status}}'
docker run --rm -v c41b-m-app_dbdata:/d:ro "$IMAGE" cat /d/etat.txt
docker volume inspect --format '{{.Name}} {{.CreatedAt}}' c41b-m-app_dbdata
c41b-m-app_dbdata c41b-m-test=1,com.docker.compose.config-hash=b84a3080856fee3da845527dc5e95d6594d6379216aca293fa56b9ad3a380d4d,com.docker.compose.project=c41b-m-app,com.docker.compose.version=5.6.1,com.docker.compose.volume=dbdata
ligne-ancienne
c41b-m-app_dbdata 2026-11-04T18:32:45Z

Le filtre de label a retrouvé l'ancien volume. La deuxième commande n'a rien affiché : aucun conteneur ne le référence. La lecture en lecture seule a confirmé ligne-ancienne, et la dernière commande relève la date de création du volume, à comparer plus bas. Sur un hôte réel, la sauvegarde de la section précédente s'impose avant tout rattachement ; la démonstration s'en passe parce que ce volume est jetable. Arrêtez ensuite le projet qui ne sert plus.

Fenêtre de terminal
docker compose -p c41b-m-app2 down

Pour rattacher l'ancien volume sans le recréer, déclarez-le external, avec son nom exact. Compose ne le crée pas et ne le gère pas : il le monte tel quel. Le service ne doit que lire, donc le montage est en lecture seule. Enregistrez ce fichier sous ext.yaml, dans le même dossier.

services:
db:
image: ${IMAGE}
command: ["sleep","600"]
volumes:
- dbdata:/var/lib/data:ro
volumes:
dbdata:
external: true
name: c41b-m-app_dbdata
Fenêtre de terminal
docker compose -p c41b-m-app3 -f ext.yaml up -d
docker compose -p c41b-m-app3 -f ext.yaml exec db cat /var/lib/data/etat.txt
docker compose -p c41b-m-app3 -f ext.yaml exec db sh -c 'echo x > /var/lib/data/n.txt'
docker volume inspect --format '{{.Name}} {{.CreatedAt}}' c41b-m-app_dbdata
Network c41b-m-app3_default Creating
Network c41b-m-app3_default Creating
Network c41b-m-app3_default Created
Network c41b-m-app3_default Created
Container c41b-m-app3-db-1 Creating
Container c41b-m-app3-db-1 Created
Container c41b-m-app3-db-1 Starting
Container c41b-m-app3-db-1 Started
ligne-ancienne
sh: can't create /var/lib/data/n.txt: Read-only file system
c41b-m-app_dbdata 2026-11-04T18:32:45Z

Regardez trois éléments. Les données sont servies, l'écriture est refusée, et la date de création est la même que celle relevée avant le rattachement (2026-11-04T18:32:45Z) : le volume n'a pas été recréé. Aucune ligne Volume ... Creating ne figure dans la sortie de up, dont certaines lignes apparaissent deux fois quand Compose écrit hors d'un terminal interactif. Un volume external n'est pas supprimé par down -v : arrêtez ce projet avec down -v et vérifiez que le volume est toujours là.

Fenêtre de terminal
docker compose -p c41b-m-app3 -f ext.yaml down -v
docker volume ls --filter name=c41b-m-app_dbdata
Container c41b-m-app3-db-1 Stopping
[...]
Network c41b-m-app3_default Removed
DRIVER VOLUME NAME
local c41b-m-app_dbdata

Pourquoi un nom de volume sans external est-il dangereux ?

Section intitulée « Pourquoi un nom de volume sans external est-il dangereux ? »

Écrire seulement name: c41b-m-app_dbdata dans la section volumes, sans external: true, semble fonctionner mais expose le volume : Compose réutilise bien le volume existant, mais il le considère comme le sien, et un down -v du nouveau projet le supprime. Le test suivant le démontre avec nom.yaml, une copie de ext.yaml sans la ligne external: true. Il supprime c41b-m-app_dbdata : sur la VM de démonstration, ce volume ne sert plus ensuite, mais ne rejouez jamais ce test sur un volume qui porte des données réelles.

Fenêtre de terminal
grep -v 'external: true' ext.yaml > nom.yaml
docker compose -p c41b-m-app4 -f nom.yaml up -d
docker compose -p c41b-m-app4 -f nom.yaml exec db cat /var/lib/data/etat.txt
docker compose -p c41b-m-app4 -f nom.yaml down -v
docker volume ls --filter name=c41b-m-app
time="2026-11-04T18:33:18Z" level=warning msg="volume \"c41b-m-app_dbdata\" already exists but was created for project \"c41b-m-app\" (expected \"c41b-m-app4\"). Use `external: true` to use an existing volume"
Network c41b-m-app4_default Creating
[...]
ligne-ancienne
Container c41b-m-app4-db-1 Removing
Volume c41b-m-app_dbdata Removing
Volume c41b-m-app_dbdata Removed
DRIVER VOLUME NAME
local c41b-m-app2_dbdata

L'avertissement du premier up nomme le problème, mais il n'arrête rien : les données sont servies. Ensuite, down -v a supprimé c41b-m-app_dbdata, le volume d'un autre projet, que la liste finale ne montre plus. Seul external: true protège le volume, et c'est pourquoi la sauvegarde précède tout rattachement. Une fois le volume supprimé, un fichier external qui le désigne ne le recrée pas, et rejouer ext.yaml le montre.

Fenêtre de terminal
docker compose -p c41b-m-app3 -f ext.yaml up -d
echo "code de sortie : $?"
external volume "c41b-m-app_dbdata" not found
code de sortie : 1

C'est un garde-fou : une faute de frappe dans le nom ne produit pas un volume vide silencieux.

Pourquoi un down sans -v laisse-t-il un volume d'image orphelin ?

Section intitulée « Pourquoi un down sans -v laisse-t-il un volume d'image orphelin ? »

Un docker compose down sans -v abandonne le volume anonyme issu d'un VOLUME d'image. Ce volume n'a pas le label com.docker.compose.project : le filtre de projet ne le retrouve pas, et aucun down -v ultérieur ne le retirera, puisque Compose ne le rattache plus au projet. La démonstration réutilise l'image c41b-m-img:1 construite plus haut, avec un service qui ne déclare aucun volume. Elle enchaîne un up, un down sans -v, un second up et un down -v.

Enregistrez ce fichier sous anon.yaml, dans le dossier du projet.

services:
app:
image: c41b-m-img:1
Fenêtre de terminal
cd ~/donnees-orphelines/projet
docker compose -p c41b-m-anon -f anon.yaml up -d
V=$(docker inspect -f '{{range .Mounts}}{{.Name}}{{end}}' c41b-m-anon-app-1)
docker compose -p c41b-m-anon -f anon.yaml down
docker volume ls --filter dangling=true --filter label=com.docker.compose.project=c41b-m-anon
docker volume inspect --format '{{json .Labels}}' "$V"
docker compose -p c41b-m-anon -f anon.yaml up -d
docker compose -p c41b-m-anon -f anon.yaml down -v
docker volume ls -q --filter name="$V"
Network c41b-m-anon_default Creating
[...]
Network c41b-m-anon_default Removed
DRIVER VOLUME NAME
{"com.docker.volume.anonymous":""}
Network c41b-m-anon_default Creating
[...]
Network c41b-m-anon_default Removed
4ca8a39ca46f1e2bedbf3171ec38709cc882d529d94977f478bccd9c6565c8f5

La première liste par label est vide : le volume n'a aucun label de projet, seulement {"com.docker.volume.anonymous":""}. Le down sans -v l'a donc laissé orphelin. Le deuxième up a créé un volume différent du premier, et down -v a bien retiré celui du conteneur courant : le volume affiché par la dernière commande est le premier, celui que le down sans -v avait abandonné. Un down -v ne retrouve que ce que Compose rattache au projet, et ce volume ne l'est pas. Il n'est repérable que par dangling=true et par son contenu. Relisez-le en lecture seule comme plus haut avant de le supprimer par son nom, et sauvegardez-le s'il porte des données.

Fenêtre de terminal
docker volume rm "$V"
4ca8a39ca46f1e2bedbf3171ec38709cc882d529d94977f478bccd9c6565c8f5

Que retire docker volume prune, avec et sans --all ?

Section intitulée « Que retire docker volume prune, avec et sans --all ? »

docker volume prune supprime les volumes orphelins, mais sans --all il ne retire que les volumes anonymes. Avec --all, il retire aussi les volumes nommés orphelins. Un volume référencé par un conteneur, même seulement créé, n'est jamais supprimé. L'invite par défaut le rappelle : WARNING! This will remove anonymous local volumes not used by at least one container.

La démonstration prépare un état précis, sous un label commun c41b-m-prune, distinct de c41b-m-test : un prune avec --all viserait sinon aussi les volumes des sections précédentes. Trois volumes anonymes étiquetés sont créés, celui d'un conteneur démarré, celui d'un conteneur seulement créé, et celui d'un conteneur supprimé sans -v, qui est le seul orphelin des trois. Côté volumes nommés : un orphelin étiqueté, un rattaché à un conteneur créé, et un sans label.

Fenêtre de terminal
docker run -d --name c41b-m-run \
--mount type=volume,dst=/a,volume-label=c41b-m-prune=1 "$IMAGE" sleep 600
docker create --name c41b-m-stopped \
--mount type=volume,dst=/a,volume-label=c41b-m-prune=1 "$IMAGE" true
docker create --name c41b-m-gone \
--mount type=volume,dst=/a,volume-label=c41b-m-prune=1 "$IMAGE" true
docker rm c41b-m-gone
docker volume create --label c41b-m-prune=1 c41b-m-n-orphan
docker volume create --label c41b-m-prune=1 c41b-m-n-used
docker volume create c41b-m-n-nolabel
docker create --name c41b-m-nuser -v c41b-m-n-used:/n "$IMAGE" true
docker volume ls --filter dangling=true --filter label=c41b-m-prune -q
3ae78b972bc95ecd2281bb9d0463f6a3c82b5f9140202a65feed5a3090807f4e
db0482628760f62726383aeda6acb107b03bf0cb9ae8fe6d84d537765caaaf2b
1559ae4ae4a1ec0a224a00adac21f7d37506c9f9684d7b2f56e4e2d0281ff753
c41b-m-gone
c41b-m-n-orphan
c41b-m-n-used
c41b-m-n-nolabel
f27cd73187864635f0035d431728c2e5e43a596df81ef7fe397226723b865321
5f55b44d6625bfa2b74c89b5a35e93e8efce8590759be3d8670b56ac8ab478bc
c41b-m-n-orphan

L'option volume-label de --mount pose un label sur le volume anonyme que Docker crée : c'est le moyen de le marquer sans lui donner de nom. Les premières lignes de la sortie sont les identifiants et les noms que les commandes de création affichent ; les deux dernières sont la liste, qui montre ce que prune peut atteindre : un anonyme et un nommé. Sans -f, docker volume prune demande confirmation, et avec un stdin vide il annule et ne supprime rien. Voici le prune sans --all :

Fenêtre de terminal
docker volume prune -f --filter label=c41b-m-prune
docker volume ls --filter label=c41b-m-prune -q
docker volume ls -q --filter name=c41b-m-n-
Deleted Volumes:
5f55b44d6625bfa2b74c89b5a35e93e8efce8590759be3d8670b56ac8ab478bc
Total reclaimed space: 0B
05da6f2d3ddd5b08c531e7550f0ac4bc8621870b65016758fb337c1362823109
c41b-m-n-orphan
c41b-m-n-used
cd23fa2890790174c466808d3df4d697ef3b07cedda6dfc4b1d3ea497ee8082d
c41b-m-n-nolabel
c41b-m-n-orphan
c41b-m-n-used

Seul l'anonyme orphelin a disparu. Restent les deux anonymes attachés à un conteneur, le nommé orphelin, le nommé attaché et le nommé sans label. Le même prune avec --all retire en plus le nommé orphelin étiqueté.

Fenêtre de terminal
docker volume prune -f --all --filter label=c41b-m-prune
docker volume ls --filter label=c41b-m-prune -q
docker volume ls -q --filter name=c41b-m-n-
Deleted Volumes:
c41b-m-n-orphan
Total reclaimed space: 0B
05da6f2d3ddd5b08c531e7550f0ac4bc8621870b65016758fb337c1362823109
c41b-m-n-used
cd23fa2890790174c466808d3df4d697ef3b07cedda6dfc4b1d3ea497ee8082d
c41b-m-n-nolabel
c41b-m-n-used

Le nommé attaché à un conteneur jamais démarré, et le nommé sans label, sont restés : le label du filtre protège le second, et la référence du conteneur protège le premier. --all est donc le geste qui supprime des données nommées : à n'utiliser qu'avec un filtre qui désigne seulement le jetable. Le prune refuse aussi le filtre dont vous auriez besoin pour viser les orphelins : dangling n'est pas un filtre valide pour prune.

Fenêtre de terminal
docker volume prune -f --all --filter label=c41b-m-prune --filter dangling=true
Error response from daemon: invalid filter 'dangling'

Un volume anonyme issu d'un VOLUME d'image n'a aucun label utilisable : un prune sans filtre toucherait tous les volumes anonymes orphelins de l'hôte, y compris ceux qui ne sont pas à vous. Supprimez-le par son nom, comme tout volume dont vous doutez.

Supprimez par nom exact, après avoir contrôlé les trois informations qui décident : les labels, le conteneur qui le référence, et le contenu. Chaque vérification écarte un cas dangereux : un label qui ne correspond pas au projet, un conteneur arrêté qui garde le volume, un contenu que personne n'a sauvegardé.

Fenêtre de terminal
docker volume inspect --format '{{json .Labels}}' c41b-m-jetable
docker ps -a --filter volume=c41b-m-jetable --format '{{.Names}} {{.Status}}'
docker run --rm -v c41b-m-jetable:/d:ro "$IMAGE" ls -la /d
docker volume rm c41b-m-jetable
{"c41b-m-role":"jetable","c41b-m-test":"1"}
total 8
drwxr-xr-x 2 root root 4096 Nov 4 18:32 .
drwxr-xr-x 1 root root 4096 Nov 4 18:33 ..
c41b-m-jetable

Les quatre commandes ont répondu dans l'ordre : le label, un conteneur absent (la sortie est vide), le contenu vide du dossier, puis le nom du volume supprimé.

Si le volume est encore référencé, docker volume rm répond volume is in use et s'arrête : ne forcez pas, supprimez d'abord le conteneur qui le garde, après avoir vérifié qu'il est lui aussi jetable. Pour que le prochain tri soit possible, posez vos propres labels à la création : --label pour docker volume create, et labels: sous le volume dans compose.yaml. Le volume c41b-m-data de cette leçon portait c41b-m-role=donnees, et c'est ce label qui l'a distingué de c41b-m-jetable d'un coup d'œil.

Supprimez d'abord les conteneurs, puis les volumes, l'image et les dossiers créés pour la leçon. Chaque commande vise le préfixe c41b-m-, jamais une catégorie entière : vos autres ressources restent en place. Le nettoyage supprime les volumes de la démonstration par leurs labels et leurs noms, et le volume anonyme de c41b-m-v1 par son nom exact.

Fenêtre de terminal
cd ~
docker ps -aq --filter name=c41b-m- | xargs -r docker rm -fv
docker volume rm "$ANON"
docker volume ls -q --filter label=c41b-m-test | xargs -r docker volume rm
docker volume ls -q --filter label=c41b-m-prune | xargs -r docker volume rm
docker volume ls -q --filter name=c41b-m- | xargs -r docker volume rm
docker volume ls -q --filter label=com.docker.compose.project=c41b-m-app | xargs -r docker volume rm
docker image ls -q --filter reference='c41b-m-*' | xargs -r docker image rm
rm -rf /tmp/c41b-m-bak ~/donnees-orphelines
docker volume ls
DRIVER VOLUME NAME

La dernière commande doit rendre la liste de départ. Le volume anonyme de c41b-m-v1 n'a aucun label : le nettoyage l'a supprimé à part, par le nom que ANON a gardé depuis le début. L'image Alpine tirée pour la leçon reste en place.

Le lab fait travailler le même enchaînement sur un projet Compose dont le service a perdu son volume. Deux volumes sont orphelins, l'un porte des données à garder, l'autre est jetable. Vous reconnectez le volume à garder au service, en lecture seule, sans le recréer, puis vous supprimez uniquement le volume jetable, après avoir lu ses labels. L'énoncé interdit docker volume prune, docker system prune et la suppression d'un volume sans label ou sans rapport avec le lab. Ses tests vérifient qu'un service tourne avec le volume d'origine monté en lecture seule, que ses labels de propriété sont intacts, que sa date de création n'a pas changé, que les données servies sont identiques à l'attendu et que le volume jetable a disparu. Les noms, les chemins et les valeurs diffèrent de ceux de la démonstration.

Un volume orphelin se juge sur son contenu, pas sur son statut.

  • Un volume est orphelin quand aucun conteneur, même arrêté ou seulement créé, ne le référence : dangling=true ne dit rien de la valeur de ses données.
  • docker rm laisse le volume anonyme du conteneur ; docker rm -v et docker run --rm le retirent avec lui.
  • Vous retrouvez un volume par ses labels, par --filter name= (sous-chaîne) ou par docker ps -a --filter volume=, puis vous lisez son contenu par un conteneur jetable monté en :ro.
  • Vous sauvegardez avec tar hors du volume, puis vous relisez l'archive avec tar -tzf et sha256sum -c : unexpected end of file signale une archive tronquée.
  • Un projet Compose renommé crée un volume vide ; l'ancien se retrouve par com.docker.compose.project et se rattache avec external: true, jamais avec name: seul, dont down -v supprime le volume.
  • Un docker compose down sans -v abandonne le volume anonyme d'un VOLUME d'image : il n'a pas de label de projet, et aucun down -v ultérieur ne le retrouve. Supprimez-le par son nom.
  • docker volume prune sans --all retire les anonymes orphelins, avec --all les nommés orphelins aussi ; un filtre label borne l'effet, et le filtre dangling est refusé.
  • Vous supprimez par nom exact, après avoir lu labels, conteneur et contenu.

Vérifiez que vous savez prévoir ce qu'une suppression laisse ou retire, et que vous retrouvez le bon volume avant d'agir.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn