SSD Nodes Learn 8GB de RAM — $66/an
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-02

Docker Compose : bind mount ou volume nommé ?

Bind mount ou volume nommé dans Docker Compose : évitez les erreurs de permissions et apprenez à inspecter, sauvegarder et migrer vos données.

Montage bind ou volume nommé : réponse courte

Les volumes Docker Compose sont de deux types. Le choix dépend de la gestion des fichiers. Utilisez un montage bind pour les fichiers que vous écrivez et lisez vous-même, comme les fichiers de configuration, les templates et les sites statiques. Utilisez un volume nommé pour les données gérées par l’application, comme les fichiers de base de données, les index de recherche et les médias importés. Un montage bind pointe vers un chemin de l’hôte que vous pouvez ouvrir dans un éditeur. Un volume nommé correspond à un espace de stockage que Docker crée et gère pour vous. Vous y accédez via Docker.

Les deux utilisent la même clé volumes: dans un service, ce qui explique la confusion. La différence se trouve à gauche des deux-points. Une valeur commençant par . ou / correspond à un chemin sur l’hôte. Il s’agit donc d’un montage bind. Toute autre valeur est un nom. Il s’agit donc d’un volume nommé, et ce nom doit également être déclaré dans le bloc volumes: de niveau supérieur.

Les deux syntaxes dans un fichier Compose

services:
  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: changeme
    volumes:
      - pgdata:/var/lib/postgresql/data
  web:
    image: nginx:1.27
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./site:/usr/share/nginx/html:ro

volumes:
  pgdata:

pgdata:/var/lib/postgresql/data est un volume nommé. ./nginx.conf:/etc/nginx/nginx.conf est un bind mount, et :ro le monte en lecture seule. C’est la valeur par défaut adaptée pour une configuration qu’un conteneur ne doit jamais réécrire. Si vous oubliez l’entrée volumes: de niveau supérieur, Compose s’arrête avec service "db" refers to undefined volume pgdata.

Démarrez les services et listez ce que Docker a créé :

docker compose up -d
docker volume ls

Le volume ne s’appelle pas pgdata. Il s’appelle <project>_pgdata, car le nom du projet correspond par défaut au nom du répertoire qui contient le fichier Compose. Un répertoire nommé myapp donne myapp_pgdata. Cela est important, car renommer le répertoire crée un nouveau volume vide et l’application semble avoir perdu ses données. Ce n’est pas le cas : l’ancien volume apparaît toujours dans la liste produite par docker volume ls. Fixez le nom avec name: dans le fichier Compose, ou définissez COMPOSE_PROJECT_NAME si le répertoire est susceptible de changer d’emplacement. Les paramètres de ce type doivent être regroupés avec vos autres fichiers d’environnement et secrets Compose.

Pourquoi les erreurs de permissions concernent uniquement les bind mounts

Il s’agit de la principale différence pratique. Elle vient d’une règle : un named volume vide lors de sa première utilisation est initialisé à partir de l’image, contrairement à un bind mount.

Lorsque Docker monte un named volume vide sur un répertoire qui contient déjà des données dans l’image, il copie ces données dans le volume, avec le propriétaire et les modes définis par l’image. L’image officielle Postgres fournit /var/lib/postgresql/data, qui appartient à son propre utilisateur postgres. Le volume appartient donc au même identifiant numérique, et la base de données démarre.

Un bind mount fonctionne à l’inverse. Le contenu présent sur l’hôte est celui que voit le conteneur, y compris le propriétaire, et le contenu de l’image à cet emplacement est masqué. Si le répertoire hôte n’existe pas, le daemon Docker le crée et s’exécute en tant que root. Vous obtenez donc un répertoire appartenant à root:root. Un processus du conteneur exécuté par un utilisateur non-root ne peut alors pas y écrire :

PermissionError: [Errno 13] Permission denied: '/data/app.db'

La correction consiste à faire correspondre les identifiants numériques. Pour un bind mount, le propriétaire est comparé selon l’identifiant utilisateur numérique, et non selon le nom, car le conteneur possède son propre /etc/passwd. Un utilisateur appelé app dans le conteneur ne correspond à rien sur l’hôte. L’UID 1000 désigne l’UID 1000 des deux côtés.

id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./data

docker compose exec web id affiche l’UID sous lequel le processus du conteneur s’exécute réellement. Faites correspondre le répertoire hôte à ce nombre, ou imposez votre propre identifiant au conteneur avec user: "1000:1000" dans le service. Imposer user: est plus simple pour une application que vous avez écrite vous-même. Modifier le propriétaire du répertoire hôte est plus sûr avec une image que vous n’avez pas écrite, car certaines images démarrent un entrypoint en tant que root, abandonnent ensuite les privilèges et attendent une propriété précise des fichiers et répertoires.

Deux autres pièges sont à connaître. Sur Fedora, RHEL et les autres systèmes où SELinux (Security-Enhanced Linux) est en mode enforcing, un bind mount est refusé tant qu’il n’a pas reçu le bon contexte. Ajoutez donc :z pour un chemin partagé entre plusieurs conteneurs, ou :Z pour un chemin qu’un seul conteneur doit utiliser, en l’écrivant sous la forme - ./data:/data:Z. De plus, un bind mount vers un fichier unique, plutôt que vers un répertoire, cesse de fonctionner lorsqu’un éditeur remplace le fichier au lieu d’écrire directement dedans, car le montage suit l’inode d’origine. Le conteneur continue alors de voir l’ancien contenu jusqu’à son redémarrage. Montez le répertoire parent lorsque le fichier est fréquemment modifié.

Performances : où l’écart est réel

Sur un serveur Linux, les deux types de montage passent par le même chemin dans le kernel. La différence de débit est donc suffisamment faible pour que vous ne choisissiez pas selon ce critère. Les volumes nommés qui utilisent le driver par défaut local résident sur le même filesystem que le reste de Docker, sous /var/lib/docker/volumes/. Un bind mount réside à l’emplacement que vous avez indiqué.

L’écart apparaît avec Docker Desktop sur macOS et Windows, où les conteneurs s’exécutent dans une machine virtuelle. Un bind mount y traverse le filesystem de l’hôte vers cette machine virtuelle, via une couche de partage de fichiers. Les workloads qui effectuent de nombreuses petites opérations sur les fichiers, comme une arborescence de dépendances Node.js ou le cache d’un framework PHP, ralentissent alors sensiblement. Les volumes nommés restent dans la machine virtuelle et ne subissent pas ce coût. C’est pourquoi de nombreux fichiers Compose de développement montent le répertoire source avec un bind mount, mais déclarent un volume nommé sur node_modules.

L’autre différence réelle concerne l’emplacement des données. Un bind mount vers /mnt/backup stocke les données sur ce disque. Un volume nommé les stocke sur le filesystem qui contient /var/lib/docker, généralement le disque root sur un VPS. Une base de données qui grossit dans un volume nommé remplit le même disque que celui où se trouvent les logs système. Vérifiez l’espace disponible avant que cela ne provoque un incident :

docker system df -v
df -h /var/lib/docker

docker system df -v répertorie tous les volumes avec leur taille et marque ceux auxquels plus aucun conteneur ne fait référence.

Inspecter un volume nommé

Un volume nommé n’est pas une boîte noire. Demandez à Docker où il se trouve :

docker volume inspect myapp_pgdata

Le champ Mountpoint indique un chemin réel sur l’hôte, généralement /var/lib/docker/volumes/myapp_pgdata/_data. Vous pouvez le lire avec sudo ls, ce qui est utile pour une vérification rapide. Ne le considérez pas comme un emplacement où modifier des fichiers. Écrire à cet endroit en tant que root recrée le problème de propriété décrit plus haut, et ce chemin est un détail du driver local que les autres drivers de volumes ne partagent pas.

La méthode sûre pour examiner le contenu consiste à utiliser un conteneur temporaire qui monte le volume :

docker run --rm -v myapp_pgdata:/vol alpine ls -la /vol

Cette méthode fonctionne avec n’importe quel driver, utilise les mêmes permissions que celles visibles par le conteneur réel et ne laisse rien derrière grâce à --rm.

Sauvegarder chaque type

Un bind mount est un répertoire ordinaire. Les outils de sauvegarde au niveau des fichiers le gèrent donc déjà. Indiquez le chemin de l’hôte comme source de sauvegarde. Cela suffit. Un volume nommé nécessite une étape supplémentaire, car l’outil doit y accéder. Montez le volume et un répertoire de l’hôte dans le même conteneur temporaire, puis créez une archive :

docker run --rm \
  -v myapp_pgdata:/data:ro \
  -v "$PWD":/backup \
  alpine tar czf /backup/pgdata.tar.gz -C /data .

Restaurez en procédant à l’inverse dans un volume vierge :

docker volume create myapp_pgdata_restored
docker run --rm \
  -v myapp_pgdata_restored:/data \
  -v "$PWD":/backup \
  alpine tar xzf /backup/pgdata.tar.gz -C /data

tar préserve la propriété numérique lorsqu’il s’exécute en tant que root dans le conteneur. Cela permet au volume restauré de rester utilisable par l’application.

Un avertissement s’applique aux deux types. Copier les fichiers d’une base de données pendant son fonctionnement crée une archive d’un état en cours de modification. La restauration peut alors produire un état corrompu. Arrêtez d’abord le service, ou effectuez un dump avec l’outil fourni par la base de données, comme dans docker compose exec -T db pg_dump -U postgres appdb > appdb.sql. Vous obtenez ainsi un fichier simple que vous pouvez ensuite inclure dans une routine de sauvegarde restic chiffrée normale avec vos fichiers compose.

Migrer un bind mount vers un volume nommé

Le déplacement est une copie, pas un changement de nom, et il prend environ une minute.

docker compose down
docker volume create myapp_pgdata
docker run --rm \
  -v "$PWD/data":/from \
  -v myapp_pgdata:/to \
  alpine sh -c 'cp -a /from/. /to/'

cp -a conserve le propriétaire, les permissions et les horodatages. L’utilisateur du conteneur qui pouvait lire l’ancien répertoire peut donc toujours lire le nouveau volume. Modifiez ensuite le service pour utiliser pgdata:/var/lib/postgresql/data, ajoutez pgdata au bloc volumes: de niveau supérieur, exécutez docker compose up -d, puis consultez les logs de l’application avant de supprimer l’ancien répertoire. Pour effectuer l’opération inverse, utilisez la même commande en permutant /from et /to.

Gardez un point à l’esprit pendant vos tests. docker compose down laisse les volumes nommés intacts, tandis que docker compose down -v supprime tous les volumes nommés déclarés par le projet, sans possibilité d’annulation. Un bind mount résiste aux deux commandes, car Docker n’a jamais géré ce répertoire. Si les commandes de cycle de vie vous sont encore peu familières, le guide de base sur Docker Compose pour un VPS les présente pas à pas.

Choisir service par service

Demandez-vous qui écrit dans le fichier. La configuration que vous modifiez dans un éditeur de texte et validez dans git doit utiliser un bind mount, monté sur :ro, car vous voulez qu’elle soit visible et versionnée. L’état de l’application que vous n’ouvrez jamais manuellement doit utiliser un volume nommé, car Docker configure correctement les permissions et les données ne dépendent pas d’un chemin de l’hôte.

Le cas mixte concerne les médias. L’application écrit dans une photothèque, mais vous la gérez également. Elle est souvent suffisamment volumineuse pour nécessiter un disque spécifique. Utilisez un bind mount vers un chemin situé sur ce disque et définissez explicitement le propriétaire une seule fois. C’est le modèle adopté par la plupart des stacks auto-hébergées : des volumes nommés pour les bases de données et les caches, des bind mounts pour la configuration et pour le répertoire volumineux auquel vous tenez.

FAQ

Quelle est la différence entre un bind mount et un named volume ?

Un bind mount mappe un chemin de l’hôte dans le conteneur. Les deux côtés voient donc le même répertoire, que vous pouvez modifier avec les outils habituels. Un named volume est un espace de stockage créé et géré par Docker. Il est référencé par son nom et déclaré dans le bloc volumes: de niveau supérieur. En pratique, utilisez les bind mounts pour la configuration que vous gérez et les named volumes pour les données que l’application gère.

Pourquoi est-ce que j’obtiens « permission denied » avec un bind mount, mais pas avec un named volume ?

Un named volume vide est initialisé à partir de l’image. Il hérite donc des propriétaires définis par l’image, et l’utilisateur du conteneur peut y écrire. Un bind mount expose le répertoire de l’hôte exactement dans son état actuel. Si Docker a dû créer ce répertoire, il l’a créé avec root comme propriétaire. Exécutez docker compose exec <service> id pour afficher l’identifiant numérique utilisé par le conteneur, puis sudo chown -R <uid>:<gid> sur le répertoire de l’hôte, ou définissez user: "1000:1000" sur le service.

Où Docker stocke-t-il les named volumes sur le disque ?

Avec le driver local par défaut, ils sont stockés sous /var/lib/docker/volumes/<volume>/_data. La commande docker volume inspect <volume> affiche le Mountpoint exact. Consultez-le si vous devez effectuer une vérification, mais écrivez-y uniquement depuis un conteneur. Une modification effectuée en tant que root sur l’hôte change les propriétaires d’une manière que le conteneur ne prévoit pas.

Comment sauvegarder un named volume ?

Lancez un conteneur temporaire avec le volume et un répertoire de l’hôte montés tous les deux, puis archivez les données de l’un vers l’autre avec docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data .. Pour une base de données, utilisez l’outil de dump fourni par la base plutôt que de copier les fichiers actifs. Une copie effectuée pendant des écritures peut être restaurée dans un état corrompu.

Est-ce que docker compose down supprime mes volumes ?

docker compose down supprime les conteneurs et les réseaux, mais conserve les named volumes. docker compose down -v supprime également tous les named volumes déclarés par le projet, de manière définitive. Les bind mounts ne sont supprimés par aucune de ces commandes, car le répertoire appartient à l’hôte et non à Docker.