SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

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 alleen 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 eigendomsrechten en modi die in de image zijn ingesteld. De officiële Postgres image wordt geleverd met /var/lib/postgresql/data in het bezit van zijn eigen postgres gebruiker, dus het volume krijgt hetzelfde numerieke id als eigenaar en de database start.

Een bind mount doet het tegenovergestelde. Wat er op de host staat, is wat de container ziet, inclusief eigendomsrechten, 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 die eigendom is van root:root. Een containerproces dat als een niet-root gebruiker draait, kan hier vervolgens niet naar schrijven:

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

De oplossing is om de nummers gelijk te trekken. Eigendom via 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.

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 dat de hostmap overeenkomt met dat nummer, of dwing de container naar uw nummer met user: "1000:1000" in de service. Het vastzetten van user: is overzichtelijker voor een applicatie die u zelf heeft geschreven. Het wijzigen van de eigenaar (chown) van de hostmap is veiliger voor een image die u niet zelf heeft geschreven, omdat sommige images een entrypoint als root starten, rechten verlagen en specifieke eigendomsrechten verwachten in de onderliggende mappen.

Er zijn nog twee valkuilen die u moet kennen. Op Fedora, RHEL en andere systemen met SELinux (security-enhanced Linux) ingeschakeld, 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. Daarnaast breekt een bind mount van een enkel bestand, in plaats van een map, wanneer een editor het bestand vervangt in plaats van de inhoud te overschrijven, omdat de mount het oorspronkelijke inode volgt. De container blijft de oude inhoud zien totdat u deze herstart. Mount 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.

Per service kiezen

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 versiebeheerd is. Applicatiestatus die u nooit handmatig opent, hoort in een named volume, omdat Docker de rechten correct instelt en de data niet afhankelijk is van een host-pad.

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. Gebruik hiervoor een bind mount naar 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 support desk 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, waardoor beide zijden dezelfde map zien en u deze met standaardtools kunt bewerken. Een named volume is opslag die Docker zelf aanmaakt en beheert; dit wordt aangeduid met een naam en gedeclareerd in het top-level volumes: blok. Het praktische onderscheid is het eigenaarschap: gebruik bind mounts voor configuratie die u zelf onderhoudt, en named volumes voor data die door de applicatie wordt beheerd.

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

Een leeg named volume wordt gevuld vanuit de image, waardoor het de rechten overneemt die in de image zijn ingesteld en de containergebruiker erin kan schrijven. Een bind mount toont de hostmap exact zoals deze is; als Docker die map zelf moest aanmaken, is deze eigendom van root. Voer docker compose exec <service> id uit om het numerieke ID van de container te zien, pas daarna sudo chown -R <uid>:<gid> toe op de hostmap, of stel user: "1000:1000" in op de service.

Waar slaat Docker named volumes op de schijf op?

Met de standaard local driver bevinden deze zich onder /var/lib/docker/volumes/<volume>/_data, en docker volume inspect <volume> toont het exacte Mountpoint. Lees dit indien u iets moet controleren, maar schrijf er alleen naar via een container. Bewerken als root op de host wijzigt het eigenaarschap op een manier 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 aangekoppeld, 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 .. Gebruik voor een database de eigen tool van de database in plaats van het kopiëren van actieve bestanden, omdat een kopie die wordt gemaakt terwijl er naar de database wordt geschreven, bij herstel tot een corrupte status kan leiden.

Verwijdert docker compose down mijn volumes?

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