SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-10-30

Docker Compose: bind mounts versus named volumes

Kies de juiste opslagmethode voor uw Docker-containers. Leer het verschil tussen bind mounts en named volumes voor configuraties, databeheer, rechten en backups.

Bind mount of named volume: het korte antwoord

Docker Compose-volumes zijn er in twee soorten, en de keuze hangt af van wie de bestanden beheert. Gebruik een bind mount voor bestanden die u zelf leest en schrijft, zoals configuraties, sjablonen en statische websites. Gebruik een named volume voor gegevens die de applicatie beheert, zoals databasebestanden, zoekindexen en geüploade media. Een bind mount verwijst naar een pad op de host dat u in een editor kunt openen. Een named volume is opslag die Docker voor u aanmaakt en bijhoudt; u benadert deze via Docker.

Beide verschijnen onder dezelfde volumes:-sleutel binnen een service, wat de reden is dat ze vaak verward worden. Het verschil zit in de linkerzijde van de dubbele punt. Een linkerzijde die begint met . of / is een hostpad, en dus een bind mount. Alles wat anders is, is een naam; dit is een named volume, en die naam moet ook worden gedeclareerd in het volumes:-blok op het hoogste niveau.

De twee syntactische vormen in een compose-bestand

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 is een named volume. ./nginx.conf:/etc/nginx/nginx.conf is een bind mount, en :ro koppelt deze alleen-lezen, wat de juiste standaardinstelling is voor configuratie die een container nooit mag overschrijven. Vergeet de volumes:-vermelding op het hoogste niveau en Compose stopt met service "db" refers to undefined volume pgdata.

Start de container en bekijk wat Docker heeft aangemaakt:

docker compose up -d
docker volume ls

Het volume heet niet pgdata. Het heet <project>_pgdata, waarbij de projectnaam standaard gelijk is aan de naam van de map die het compose-bestand bevat. Een map genaamd myapp resulteert in myapp_pgdata. Dit is van belang omdat het hernoemen van de map een nieuw, leeg volume oplevert, waardoor het lijkt alsof de applicatie haar gegevens is verloren. Dat is niet het geval: het oude volume staat nog steeds in de lijst van docker volume ls. Zet de naam vast met name: in het compose-bestand, of stel COMPOSE_PROJECT_NAME in als de map mogelijk wordt verplaatst. Dergelijke instellingen horen bij uw overige Compose omgevingsbestanden en secrets.

Waarom rechtenfouten meestal optreden bij bind mounts

Dit is het grootste praktische verschil, en het komt voort uit één regel: een named volume dat bij het eerste gebruik leeg is, wordt gevuld vanuit de image, terwijl een bind mount dat nooit wordt.

Wanneer Docker een leeg named volume over een map monteert die al inhoud heeft in de image, kopieert het die inhoud naar het volume, met de eigenaar en rechten zoals ingesteld in de image. De officiële Postgres-image wordt geleverd met /var/lib/postgresql/data, eigendom van de eigen postgres-gebruiker, dus het volume krijgt diezelfde numerieke id als eigenaar en de database start.

Dat vullen is de enige voorsprong die een named volume krijgt. Docker beheert waar het volume zich bevindt, maar containers lezen en schrijven erin met standaard bestandssysteemoperaties, dus eigenaarschap en rechten worden net zo afgedwongen als bij een bind mount. Een named volume dat op een pad wordt gemonteerd dat niet in de image voorkomt, of dat voor het eerst wordt gevuld door een andere image of gebruiker, krijgt geen eigenaarschap van de image, en een proces dat niet als root draait kan daar ook worden geweigerd. De id-controle hieronder werkt voor beide typen.

Een bind mount doet het tegenovergestelde. Wat op de host staat, ziet de container, inclusief eigenaarschap, en de inhoud van de image op dat pad is verborgen. Als de hostmap niet bestaat, maakt de Docker-daemon deze aan. De daemon draait als root, dus u krijgt een map waarvan root:root de eigenaar is. Een containerproces dat als een niet-rootgebruiker draait, kan daar vervolgens niet naar schrijven:

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

De oplossing is om de nummers gelijk te trekken. Eigenaarschap over een bind mount wordt vergeleken op basis van het numerieke user id, niet op naam, omdat de container zijn eigen /etc/passwd heeft. Een gebruiker genaamd app binnen de container betekent niets op de host. Uid 1000 betekent uid 1000 aan beide kanten, tenzij de daemon draait met user-namespace remapping (userns-remap, ingesteld in /etc/docker/daemon.json of via de dockerd-opdrachtregel). Remapping verschuift elk container-id naar een onderliggend bereik op de host. In het voorbeeld van Docker dat begint bij 231072, is container-uid 0 gelijk aan host-uid 231072, dus container-uid 1000 is host-uid 232072.

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

docker compose exec web id toont het uid waaronder het containerproces daadwerkelijk draait. Zorg zonder remapping dat de hostmap overeenkomt met dat nummer, of pin de container vast op uw nummer met user: "1000:1000" in de service. Met ingeschakelde remapping kan user: de container niet vastpinnen op uw hostnummer, omdat het id dat u instelt ook wordt verschoven: user: "1000:1000" komt in dat voorbeeld bereik nog steeds uit op host-uid 232072. Voer een chown uit op de hostmap naar het gemapte host-id (uw eigen bereik staat in /etc/subuid en /etc/subgid). De documentatie van Docker noemt alleen --userns=host op docker container create, run of exec als manier om remapping voor een enkele container uit te schakelen. Het vastpinnen van user: is schoner voor een applicatie die u zelf heeft geschreven. Het uitvoeren van een chown op de hostmap is veiliger voor een image die u niet zelf heeft geschreven, omdat sommige images een entrypoint als root starten, rechten laten vallen en specifiek eigenaarschap verwachten.

Twee andere valkuilen zijn belangrijk om te kennen. Op Fedora, RHEL en andere systemen waar SELinux (security-enhanced Linux) actief is, wordt een bind mount geweigerd totdat deze opnieuw is gelabeld. Voeg daarom :z toe voor een pad dat wordt gedeeld tussen containers, of :Z voor een pad dat slechts door één container moet worden gebruikt, geschreven als - ./data:/data:Z. Een bind mount van een enkel bestand, in plaats van een map, werkt niet meer wanneer een editor het bestand vervangt in plaats van het op de bestaande locatie te overschrijven, omdat de mount de oorspronkelijke inode volgt. De container blijft de oude inhoud zien totdat u deze herstart. Monteer de bovenliggende map wanneer het bestand vaak wordt bewerkt.

Prestaties: waar het verschil merkbaar is

Op een Linux-server doorlopen beide typen hetzelfde kernelpad, waardoor het verschil in doorvoer klein genoeg is om dit niet als basis voor uw keuze te gebruiken. Named volumes die de standaard local-driver gebruiken, bevinden zich op hetzelfde bestandssysteem als de rest van Docker, onder /var/lib/docker/volumes/, terwijl een bind mount zich bevindt op de locatie die u heeft opgegeven.

Het verschil wordt duidelijk op Docker Desktop voor macOS en Windows, waar containers binnen een virtuele machine draaien. Een bind mount gaat daar vanaf het host-bestandssysteem naar de virtuele machine via een laag voor het delen van bestanden. Workloads met veel kleine bestandsbewerkingen, zoals een Node.js-dependency tree of een cache van een PHP-framework, vertragen hierdoor merkbaar. Named volumes blijven binnen de virtuele machine en hebben deze overhead niet. Dit is de reden waarom veel compose-bestanden voor ontwikkeling de bronmap bind-mounten, maar een named volume declareren voor node_modules.

Het andere wezenlijke verschil is waar de data wordt opgeslagen. Een bind mount naar /mnt/backup plaatst data op die specifieke schijf. Een named volume komt terecht op het bestandssysteem waar /var/lib/docker zich bevindt; op een VPS is dit doorgaans de root-schijf. Een database die groeit binnen een named volume vult dezelfde schijf als waar uw systeemlogs op staan. Controleer dit voordat het tot een incident leidt:

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

docker system df -v toont elk volume met de bijbehorende grootte en markeert de volumes waar geen enkele container meer naar verwijst.

Inspecteren van een named volume

Een named volume is geen black box. Vraag aan Docker waar het zich bevindt:

docker volume inspect myapp_pgdata

Het veld Mountpoint geeft een werkelijk hostpad aan, meestal /var/lib/docker/volumes/myapp_pgdata/_data. U kunt dit uitlezen met sudo ls, wat nuttig is voor een snelle controle. Beschouw dit niet als een locatie om bestanden te bewerken. Schrijven naar deze locatie als root veroorzaakt opnieuw het eigendomsprobleem dat hierboven is beschreven, en het pad is een detail van de local-driver dat andere volume-drivers niet delen.

De veilige manier om de inhoud te bekijken is via een tijdelijke container die het volume koppelt:

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

Dit werkt voor elke driver, hanteert dezelfde rechten als de werkelijke container en laat niets achter dankzij --rm.

Back-ups maken per type

Een bind mount is een gewone map, dus elk back-uptool op bestandsniveau kan deze verwerken. Wijs de back-up naar het pad op de host en u bent klaar. Een named volume vereist één extra stap, omdat de tool toegang moet krijgen tot de inhoud. Mount het volume en een hostmap in dezelfde tijdelijke container en schrijf vervolgens een archief:

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

Herstel de data door het proces om te keren naar een nieuw 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 behoudt numeriek eigenaarschap wanneer het als root binnen de container wordt uitgevoerd; dit zorgt ervoor dat het herstelde volume bruikbaar blijft voor de applicatie.

Eén waarschuwing geldt voor beide typen. Het kopiëren van databasebestanden terwijl de database actief is, levert een archief op van een bewegend doelwit, wat kan leiden tot een corrupt herstel. Stop eerst de service of maak een dump via de eigen tool van de database, zoals in docker compose exec -T db pg_dump -U postgres appdb > appdb.sql. Dit levert een plat bestand op dat u vervolgens kunt opnemen in een normale versleutelde restic back-upprocedure, samen met uw compose-bestanden.

Migreren van een bind mount naar een named volume

De verplaatsing is een kopieeractie, geen hernoeming, en duurt ongeveer een minuut.

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 behoudt eigenaarschap, permissies en tijdstempels, zodat de containergebruiker die de oude map kon lezen, ook het nieuwe volume kan lezen. Wijzig vervolgens de service om pgdata:/var/lib/postgresql/data te gebruiken, voeg pgdata toe aan het top-level volumes:-blok, voer docker compose up -d uit en lees de logs van de applicatie voordat u de oude map verwijdert. De omgekeerde weg gebruikt hetzelfde commando waarbij /from en /to zijn omgewisseld.

Houd tijdens het testen rekening met één ding. docker compose down laat named volumes ongemoeid, maar docker compose down -v verwijdert elk named volume dat in het project is gedeclareerd, en dit kan niet ongedaan worden gemaakt. Een bind mount overleeft beide acties, omdat Docker de eigenaar van die map nooit was. Als de lifecycle-commando's nog nieuw voor u zijn, doorloopt de basisgids voor Docker Compose op een VPS deze stappen.

Keuzes maken, per service

Bepaal wie het bestand schrijft. Configuratie die u in een teksteditor bewerkt en naar git commit, hoort in een bind mount, gekoppeld aan :ro, omdat u wilt dat deze zichtbaar en geversioneerd is. Applicatiestatus die u nooit handmatig opent, hoort in een named volume, omdat Docker de rechten vanuit de image toewijst en de data niet afhankelijk is van een hostpad.

Het gemengde geval betreft media. Een fotobibliotheek wordt door de applicatie geschreven, maar ook door u beheerd, en is vaak groot genoeg om een specifieke schijf te vereisen. Koppel deze via een bind mount aan een pad op die schijf en stel de eigendomsrechten eenmalig bewust in. Dit is het patroon waar de meeste self-hosted stacks op uitkomen: named volumes voor databases en caches, bind mounts voor configuratie en voor de grote mappen die voor u van belang zijn. Een supportdesk zoals Chatwoot draaiend op een VPS komt precies hierop uit, met Postgres in een named volume en de geüploade bijlagen op een pad waar u een back-up naar kunt verwijzen.

FAQ

Wat is het verschil tussen een bind mount en een named volume?

Een bind mount koppelt een pad op de host aan de container, zodat beide kanten dezelfde map zien en u deze met normale tools kunt bewerken. Een named volume is opslag die Docker aanmaakt en beheert, aangeduid met een naam en gedeclareerd in het volumes:-blok op het hoogste niveau. Het praktische onderscheid is eigenaarschap: bind mounts voor configuratie die u beheert, named volumes voor data die de applicatie beheert.

Waarom krijg ik "permission denied" bij een bind mount maar niet bij een named volume?

Een leeg named volume wordt gevuld vanuit de image, dus het neemt meestal het eigenaarschap over dat de image heeft ingesteld en de containergebruiker kan erin schrijven. Het dwingt nog steeds rechten af, dus het kan op dezelfde manier mislukken als de image niets op dat pad heeft staan. Een bind mount toont de hostmap precies zoals deze is, en als Docker die map moest aanmaken, is root de eigenaar. Voer docker compose exec <service> id uit om het numerieke id te zien dat de container gebruikt, voer daarna sudo chown -R <uid>:<gid> uit op de hostmap, of stel user: "1000:1000" in op de service wanneer de daemon geen user namespaces remapt. Als de daemon user-namespace remapping gebruikt, voer dan een chown uit naar het gemapte host-id, omdat de id's van de container op de host verschoven zijn en user: dat niet verandert.

Waar slaat Docker named volumes op de schijf op?

Met de standaard local-driver bevinden ze zich onder /var/lib/docker/volumes/<volume>/_data, en docker volume inspect <volume> toont het exacte Mountpoint. Lees dit als u iets moet controleren, maar schrijf er alleen naar via een container, omdat bewerken als root op de host het eigenaarschap verandert op manieren die de container niet verwacht.

Hoe maak ik een back-up van een named volume?

Start een tijdelijke container met zowel het volume als een hostmap gemonteerd, en archiveer vervolgens van de ene naar de andere locatie met docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data .. Voor een database kunt u beter de eigen tool van de database gebruiken in plaats van live bestanden te kopiëren, omdat een bestandskopie die wordt gemaakt terwijl er naar geschreven wordt, bij herstel tot een corrupte status kan leiden.

Verwijdert docker compose down mijn volumes?

docker compose down verwijdert containers en netwerken en laat named volumes staan. docker compose down -v verwijdert ook elk named volume dat in het project is gedeclareerd, permanent. Bind mounts worden door geen van beide opdrachten verwijderd, omdat die map toebehoort aan de host en niet aan Docker.

Was dit artikel nuttig?