SSD Nodes Learn 8GB RAM — $66/jaar
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-02

Docker Compose-volumes: bind mount of named volume?

Lees wanneer u een bind mount of named volume gebruikt voor configuratie en data, plus valkuilen met bestandsrechten en hoe u volumes inspecteert, back-upt en migreert.

Bind mount of named volume: het korte antwoord

Docker Compose-volumes zijn er in twee soorten. De keuze bepaalt wie eigenaar is van de bestanden. Gebruik een bind mount voor bestanden die u zelf schrijft en leest, zoals configuratie, templates en statische sites. 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 beheert. U benadert deze opslag via Docker.

Beide staan onder dezelfde volumes:-sleutel binnen een service. Daarom worden ze vaak door elkaar gehaald. Het verschil zit aan de linkerkant van de dubbele punt. Een linkerkant die begint met . of / is een hostpad. Het gaat dus om een bind mount. Al het andere is een naam. Het gaat dus om een named volume. Die naam moet ook worden gedeclareerd in het blok volumes: op het hoogste niveau.

De twee syntaxisvormen 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 benoemd volume. ./nginx.conf:/etc/nginx/nginx.conf is een bind-mount en :ro koppelt dit alleen-lezen, wat de juiste standaard is voor configuratie die een container nooit mag overschrijven. Als u de entry op hoofdniveau volumes: vergeet, stopt Compose met service "db" refers to undefined volume pgdata.

Start de omgeving en geef weer wat Docker heeft aangemaakt:

docker compose up -d
docker volume ls

Het volume heet niet pgdata. Het heet <project>_pgdata, waarbij de projectnaam standaard de naam is van de directory waarin het Compose-bestand staat. Een directory met de naam myapp geeft myapp_pgdata. Dit is belangrijk omdat het hernoemen van de directory een nieuw, leeg volume oplevert en het lijkt alsof de applicatie zijn gegevens kwijt is. Dat is niet het geval: het oude volume wordt nog steeds weergegeven door docker volume ls. Leg de naam vast met name: in het Compose-bestand, of stel COMPOSE_PROJECT_NAME in als de directory mogelijk wordt verplaatst. Instellingen zoals deze horen bij uw andere Compose-omgevingsbestanden en secrets.

Waarom permissiefouten alleen bij bind mounts optreden

Dit is het belangrijkste praktische verschil. Het komt door één regel: een named volume dat bij het eerste gebruik leeg is, wordt gevuld vanuit de image, terwijl een bind mount dat nooit doet.

Wanneer Docker een leeg named volume koppelt over een directory die in de image al inhoud bevat, kopieert Docker die inhoud naar het volume. Daarbij blijven de ownership en permissies behouden die de image heeft ingesteld. De officiële Postgres-image levert /var/lib/postgresql/data met ownership van de eigen postgres-gebruiker. Het volume krijgt daardoor dezelfde numerieke id als eigenaar en de database start.

Een bind mount werkt andersom. De inhoud op de host is wat de container ziet, inclusief de ownership. De inhoud van de image op dat pad wordt verborgen. Als de directory op de host niet bestaat, maakt de Docker-daemon deze aan. De daemon draait als root, waardoor u een directory krijgt met root:root als eigenaar. 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 dat de numerieke waarden overeenkomen. Ownership via een bind mount wordt vergeleken op basis van de numerieke user id, niet op basis van de naam. De container heeft namelijk een eigen /etc/passwd. Een gebruiker met de naam app in de container heeft op de host geen betekenis. Uid 1000 betekent aan beide kanten uid 1000.

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

docker compose exec web id toont de uid waaronder het containerproces daadwerkelijk draait. Pas de directory op de host aan deze waarde aan, of leg met user: "1000:1000" in de service vast dat de container uw nummer gebruikt. user: vastleggen is voor een applicatie die u zelf hebt geschreven de meest overzichtelijke oplossing. De directory op de host chownen is veiliger voor een image die u niet zelf hebt geschreven. Sommige images starten een entrypoint als root, verlagen daarna de privileges en verwachten specifieke ownership onder de betreffende directory.

Er zijn nog twee aandachtspunten. Op Fedora, RHEL en andere systemen waarop SELinux (security-enhanced Linux) enforcing is ingeschakeld, wordt een bind mount geweigerd totdat deze opnieuw is gelabeld. Voeg daarom :z toe voor een pad dat tussen containers wordt gedeeld, of :Z voor een pad dat slechts door één container mag worden gebruikt, geschreven als - ./data:/data:Z. Een bind mount van één bestand in plaats van een directory werkt bovendien niet goed wanneer een editor het bestand vervangt in plaats van het ter plaatse te wijzigen. Dit komt doordat de mount de oorspronkelijke inode blijft volgen. De container blijft de oude inhoud zien totdat u deze opnieuw start. Koppel de bovenliggende directory wanneer het bestand vaak wordt bewerkt.

Prestaties: waar het verschil werkelijk merkbaar is

Op een Linux-server gebruiken beide typen hetzelfde kernelpad. Het verschil in doorvoer is daarom klein genoeg om uw keuze daar niet op te baseren. Named volumes met het standaardstuurprogramma local staan op hetzelfde bestandssysteem als de rest van Docker, onder /var/lib/docker/volumes/. Een bind mount staat op de locatie die u hebt opgegeven.

Het verschil wordt merkbaar in Docker Desktop voor macOS en Windows, waar containers in een virtuele machine draaien. Een bind mount loopt daar via een laag voor het delen van bestanden van het bestandssysteem van de host naar die virtuele machine. Workloads met veel kleine bestandsbewerkingen, zoals een afhankelijkheidsstructuur van Node.js of een cache van een PHP-framework, worden daardoor merkbaar trager. Named volumes blijven in de virtuele machine en hebben die overhead niet. Daarom koppelen veel development-composebestanden de bronmap als bind mount, maar declareren ze een named volume voor node_modules.

Het andere werkelijke verschil is de locatie waar de bytes worden opgeslagen. Met een bind mount naar /mnt/backup wordt de data op die schijf opgeslagen. Een named volume komt terecht op het bestandssysteem waarop /var/lib/docker staat. Op een VPS is dat meestal de root-schijf. Een database die in een named volume groeit, vult dezelfde schijf waarop uw systeemlogboeken staan. Controleer dit voordat het een incident veroorzaakt:

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

docker system df -v geeft elk volume met de grootte weer en markeert de volumes waarnaar geen enkele container meer verwijst.

Een benoemd volume inspecteren

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

docker volume inspect myapp_pgdata

Het veld Mountpoint bevat een echt hostpad, meestal /var/lib/docker/volumes/myapp_pgdata/_data. U kunt het lezen met sudo ls. Dat is handig voor een snelle controle. Gebruik dit pad niet om bestanden te bewerken. Als u daar als root schrijft, ontstaat het hierboven beschreven eigendomsprobleem opnieuw. Bovendien is het pad een detail van de driver local. Andere volumestuurprogramma's delen dit detail niet.

Gebruik een tijdelijke container die het volume koppelt om de inhoud veilig te bekijken:

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

Dit werkt met elke driver. De container ziet dezelfde machtigingen als de echte container. Er blijft niets achter dankzij --rm.

Elk type back-uppen

Een bind mount is een gewone directory. Elke back-uptool op bestandsniveau verwerkt deze daarom al. Wijs de back-up naar het pad op de host en u bent klaar. Voor een named volume is één extra stap nodig, omdat de tool toegang tot het volume moet krijgen. Mount het volume en een directory op de host 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 het archief door de bewerking omgekeerd uit te voeren 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 dit als root in de container wordt uitgevoerd. Daardoor blijft het herstelde volume bruikbaar voor de applicatie.

Voor beide typen geldt dezelfde waarschuwing. Als u de bestanden van een database kopieert terwijl de database actief is, maakt u een archief van een veranderende toestand. Dit kan worden hersteld naar een corrupte toestand. Stop de service eerst of maak een dump met de eigen tool van de database, zoals in docker compose exec -T db pg_dump -U postgres appdb > appdb.sql. Daarmee maakt u een normaal bestand dat u vervolgens kunt opnemen in een normale versleutelde restic-back-uproutine samen met uw compose-bestanden.

Een bind mount migreren naar een benoemd 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, modi en tijdstempels. De containergebruiker die de oude directory kon lezen, kan daarom ook het nieuwe volume lezen. Wijzig de service vervolgens zodat deze pgdata:/var/lib/postgresql/data gebruikt, voeg pgdata toe aan het bovenste volumes:-blok, voer docker compose up -d uit en lees de logs van de applicatie voordat u de oude directory verwijdert. In de andere richting gebruikt u dezelfde opdracht, maar verwisselt u /from en /to.

Houd tijdens het testen rekening met één punt. docker compose down laat benoemde volumes ongemoeid, maar docker compose down -v verwijdert elk benoemd volume dat het project declareert. Dit kan niet ongedaan worden gemaakt. Een bind mount blijft na beide opdrachten bestaan, omdat Docker nooit eigenaar van die directory was. Als de lifecycle-opdrachten nog nieuw voor u zijn, behandelt de basisgids voor Docker Compose op een VPS deze opdrachten stap voor stap.

Een keuze per service

Bepaal wie het bestand schrijft. Configuratie die u in een teksteditor bewerkt en naar git commit, hoort in een bind mount die wordt aangekoppeld op :ro. Zo blijft de configuratie zichtbaar en wordt deze voorzien van versiebeheer. Applicatiestatus die u nooit handmatig opent, hoort in een named volume. Docker stelt de machtigingen dan correct in en de gegevens zijn niet afhankelijk van een pad op de host.

Media vormt een tussengeval. Een fotobibliotheek wordt door de applicatie geschreven, maar ook door u beheerd. De bibliotheek is vaak groot genoeg om er een specifieke schijf voor te gebruiken. Koppel deze als bind mount aan een pad op die schijf en stel het eigendom 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 map die voor u belangrijk is.

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. Beide kanten zien dan dezelfde directory en u kunt deze met normale hulpprogramma's bewerken. Een named volume is opslag die Docker maakt en beheert. U verwijst ernaar met een naam en declareert deze in het blok volumes: op het hoogste niveau. Het praktische verschil betreft eigendom: gebruik bind mounts voor configuratie die u beheert en named volumes voor gegevens die de applicatie beheert.

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

Een lege named volume wordt vanuit de image gevuld. Daardoor neemt deze het eigendom over dat in de image is ingesteld en kan de containergebruiker erin schrijven. Een bind mount toont de hostdirectory precies zoals deze is. Als Docker die directory moest maken, werd deze eigendom van root. Voer docker compose exec <service> id uit om de numerieke id te zien die de container gebruikt. Gebruik daarna sudo chown -R <uid>:<gid> voor de hostdirectory of stel user: "1000:1000" in voor de service.

Waar slaat Docker named volumes op schijf op?

Met de standaarddriver local worden deze opgeslagen onder /var/lib/docker/volumes/<volume>/_data. Met docker volume inspect <volume> wordt de exacte Mountpoint weergegeven. Lees deze als u iets moet controleren, maar schrijf er alleen via een container naartoe. Als u als root op de host bewerkingen uitvoert, verandert het eigendom op een manier die de container niet verwacht.

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

Start een tijdelijke container waarin zowel de volume als een hostdirectory zijn gekoppeld. Archiveer vervolgens van de ene naar de andere 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 om een dump te maken in plaats van live bestanden te kopiëren. Een bestandskopie die wordt gemaakt terwijl er nog schrijfbewerkingen plaatsvinden, kan namelijk in een beschadigde toestand worden teruggezet.

Verwijdert docker compose down mijn volumes?

docker compose down verwijdert containers en netwerken en laat named volumes staan. docker compose down -v verwijdert ook elke named volume die het project declareert, permanent. Bind mounts worden door geen van beide opdrachten verwijderd, omdat die directory bij de host hoort en niet bij Docker.