SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-10-23

Docker Compose : bind mount ou named volume ?

Choisissez entre bind mount et named volume selon vos fichiers : permissions, inspection, sauvegarde et migration des données Docker Compose, avec les bonnes syntaxes.

Bind mount ou named volume : réponse courte

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

Les deux types 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 qui commence par . ou / désigne un chemin sur l’hôte : il s’agit donc d’un bind mount. Toute autre valeur est un nom : il s’agit d’un named volume, 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 le bon réglage par défaut 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.

Lancez les services et listez les éléments créés par Docker :

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 produit myapp_pgdata. Ce point est important : si vous renommez le répertoire, vous obtenez 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 renvoyée 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 d’être déplacé. Ces paramètres doivent figurer avec vos autres fichiers d’environnement et secrets Compose.

Pourquoi les erreurs de permissions touchent surtout les bind mounts

C’est la principale différence pratique. Elle découle surtout 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.

Cette initialisation est le seul avantage initial d’un named volume. Docker gère l’emplacement du volume, mais les conteneurs le lisent et l’écrivent avec des opérations normales sur le système de fichiers. Le propriétaire et les modes sont donc appliqués comme avec un bind mount. Un named volume monté sur un chemin absent de l’image, ou rempli pour la première fois par une autre image ou un autre utilisateur, n’hérite d’aucun propriétaire de l’image. Un processus non-root peut donc aussi y être bloqué. La vérification id ci-dessous fonctionne avec les deux types de montage.

Un bind mount fait l’inverse. Le conteneur voit exactement ce qui se trouve sur l’hôte, y compris le propriétaire, et le contenu de l’image présent à ce chemin est masqué. Si le répertoire de l’hôte n’existe pas, le Docker daemon le crée. Le daemon s’exécute en tant que root, ce qui produit un répertoire appartenant à root:root. Un processus du conteneur exécuté avec 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. Le propriétaire d’un bind mount 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, sauf si le daemon utilise le remappage des user namespaces (userns-remap, défini dans /etc/docker/daemon.json ou sur la ligne de commande dockerd). Le remappage décale chaque identifiant du conteneur vers une plage subordinate sur l’hôte. Dans la plage d’exemple de Docker, qui commence à 231072, l’UID 0 du conteneur correspond à l’UID 231072 sur l’hôte. L’UID 1000 du conteneur correspond donc à l’UID 232072 sur l’hôte.

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

docker compose exec web id affiche l’UID avec lequel le processus du conteneur s’exécute réellement. Faites correspondre le répertoire de l’hôte à ce nombre, ou imposez votre identifiant au conteneur avec user: "1000:1000" dans le service. Lorsque le remappage est activé, attribuez plutôt le répertoire de l’hôte à l’identifiant hôte correspondant (232072 pour l’UID 1000 dans cette plage d’exemple ; votre propre plage se trouve dans /etc/subuid et /etc/subgid). Notez que user: ne désactive pas le remappage. Imposer user: est plus propre pour une application que vous avez écrite vous-même. Modifier le propriétaire du répertoire de l’hôte est plus sûr avec une image que vous n’avez pas écrite, car certaines images démarrent leur 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 importants. 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 été relabellisé. Ajoutez donc :z pour un chemin partagé entre plusieurs conteneurs, ou :Z pour un chemin réservé à un seul conteneur, en l’écrivant sous la forme - ./data:/data:Z. Par ailleurs, un bind mount portant sur un fichier unique, plutôt que sur un répertoire, cesse de fonctionner lorsqu’un éditeur remplace le fichier au lieu de l’écrire sur place, 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 : là où l’écart est réel

Sur un serveur Linux, les deux types de montages 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 sur ce seul critère. Les named volumes utilisant le driver local par défaut résident sur le même système de fichiers 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 système de fichiers de l’hôte vers cette machine virtuelle, via une couche de partage de fichiers. Les workloads comportant de nombreuses opérations sur de petits fichiers, comme un arbre de dépendances Node.js ou le cache d’un framework PHP, ralentissent alors sensiblement. Les named volumes restent dans la machine virtuelle et n’ont pas ce coût. C’est pourquoi de nombreux fichiers Compose de développement montent le répertoire source avec un bind mount, tout en déclarant un named volume sur node_modules.

L’autre différence réelle concerne l’emplacement des données. Un bind mount vers /mnt/backup place les données sur ce disque. Un named volume utilise le système de fichiers qui contient /var/lib/docker, généralement le disque racine sur un VPS. Une base de données qui grossit dans un named volume remplit le même disque que celui qui contient les journaux du système. Vérifiez-le avant qu’un incident ne survienne :

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

docker system df -v liste tous les volumes avec leur taille et indique ceux auxquels plus aucun conteneur ne fait référence. Si le disque racine sature et que vous envisagez de déplacer les données de Docker vers un autre disque, sachez que depuis Docker Engine 29 une installation neuve range les images dans /var/lib/containerd et non plus sous /var/lib/docker : le guide où Docker stocke ses images et comment les déplacer indique quels dossiers déplacer selon votre cas.

Inspection d’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 fournit un chemin réel sur l’hôte, normalement /var/lib/docker/volumes/myapp_pgdata/_data. Vous pouvez le lire avec sudo ls, ce qui est utile pour une vérification rapide. Ne l’utilisez pas pour modifier des fichiers. Écrire à cet emplacement avec root recrée le problème de propriétaire décrit plus haut. De plus, ce chemin est une caractéristique du driver local que les autres drivers de volume ne partagent pas.

La méthode sûre pour consulter 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. Elle utilise les mêmes permissions que celles vues par le conteneur réel et ne laisse rien derrière elle grâce à --rm.

Sauvegarder chaque type

Un bind mount est un répertoire ordinaire. Tous les outils de sauvegarde au niveau des fichiers le prennent donc déjà en charge. Indiquez le chemin de l’hôte comme source de sauvegarde. C’est tout. Un named volume nécessite une étape supplémentaire, car l’outil doit accéder à son contenu. 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 les données en inversant l’opération dans un nouveau volume :

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. C’est ce qui permet à l’application d’utiliser le volume restauré.

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 qui évolue en permanence. 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 ordinaire que vous pouvez ensuite inclure dans une routine de sauvegarde restic chiffrée normale avec vos fichiers Compose.

Migration d'un bind mount vers un volume nommé

Le déplacement est une copie, pas un renommage, et cela 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 les propriétaires, les modes et les horodatages, de sorte que l'utilisateur du conteneur qui pouvait lire l'ancien répertoire puisse toujours lire le nouveau volume. Ensuite, modifiez le service pour utiliser pgdata:/var/lib/postgresql/data, ajoutez pgdata au bloc volumes: de niveau supérieur, exécutez docker compose up -d, et lisez les journaux de l'application avant de supprimer l'ancien répertoire. La procédure inverse utilise la même commande avec /from et /to inversés.

Gardez une chose à l'esprit pendant vos tests. docker compose down laisse les volumes nommés intacts, mais docker compose down -v supprime chaque volume nommé déclaré par le projet, et il n'y a pas d'annulation possible. Un bind mount survit aux deux, car Docker n'a jamais été propriétaire de ce répertoire. Si les commandes de cycle de vie sont encore nouvelles pour vous, le guide des bases de Docker Compose pour un VPS les détaille.

Choisir service par service

Demandez-vous qui écrit dans le fichier. Une configuration que vous modifiez dans un éditeur de texte et validez dans git doit être placée dans un bind mount monté en :ro, car vous voulez qu’elle soit visible et versionnée. Les données d’application que vous n’ouvrez jamais manuellement doivent être placées dans un volume nommé, car Docker initialise les permissions à partir de l’image et les données ne dépendent pas d’un chemin de l’hôte.

Le cas mixte concerne les médias. Une photothèque est écrite par l’application, mais vous la gérez également. Elle est souvent assez volumineuse pour justifier l’utilisation d’un disque précis. Montez-la avec un bind mount vers un chemin situé sur ce disque, puis définissez explicitement son propriétaire une seule fois. C’est le modèle auquel aboutissent 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. Un service de support comme Chatwoot exécuté sur un VPS correspond exactement à ce modèle, avec Postgres dans un volume nommé et les pièces jointes téléversées dans un chemin que vous pouvez cibler avec une sauvegarde.

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 alors 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 « permission denied » apparaît-il avec un bind mount, mais pas avec un named volume ?

Un named volume vide est initialisé à partir de l’image. Il hérite donc généralement des propriétaires définis par l’image, et l’utilisateur du conteneur peut y écrire. Les permissions s’appliquent toujours. L’opération peut donc échouer si l’image ne contient rien à cet emplacement. Un bind mount affiche exactement le répertoire de l’hôte. 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 connaître 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. Si le daemon utilise le remappage des user namespaces, utilisez plutôt l’identifiant hôte correspondant dans chown, car les identifiants du conteneur sont décalés sur l’hôte et user: ne modifie pas ce décalage.

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, et docker volume inspect <volume> affiche le Mountpoint exact. Consultez-le si vous devez effectuer une vérification, mais écrivez dans ce répertoire uniquement depuis un conteneur. Une modification 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 en cours d’utilisation. 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.

Cet article vous a-t-il été utile ?