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

Wat doen PUID en PGID in Docker Compose?

PUID en PGID zijn geen Docker-instellingen maar een conventie van linuxserver.io. Ontdek waarom bestanden als 911:911 verschijnen en hoe u dit correct configureert.

Wat PUID en PGID daadwerkelijk zijn

PUID en PGID zijn twee omgevingsvariabelen die bepaalde container-images bij het opstarten uitlezen. Docker zelf kijkt hier nooit naar. Het is een conventie die wordt gebruikt door images van linuxserver.io en enkele anderen; een image dat niet is geschreven om deze te lezen, negeert ze geruisloos.

Binnen een image van linuxserver.io bevindt zich een gebruiker genaamd abc, die tijdens het build-proces is aangemaakt met een UID (user ID) 911 en GID (group ID) 911. De container start als root, voert de init-scripts uit, en een van die scripts wijzigt het nummer van die gebruiker voordat er iets anders gebeurt:

groupmod -o -g "${PGID}" abc
usermod -o -u "${PUID}" abc

De vlag -o staat een ID toe dat elders al in gebruik is. Daarna verlaagt het init-proces de rechten en voert de applicatie uit als abc. De variabele PUID=1000 bereikt Docker dus nooit. De variabele wijzigt het nummer van een gebruiker binnen de container voordat de applicatie start, wat betekent dat elk bestand dat de applicatie schrijft op uw schijf terechtkomt met eigenaar 1000. Laat u PUID oningesteld, dan behoudt abc de waarde 911; dit is de reden waarom een niet-geconfigureerde bind mount volloopt met bestanden die eigendom zijn van 911:911.

Bepaal uw twee getallen met id

Voer dit uit op de host, als de gebruiker die eigenaar is van de datamappen:

id
uid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),988(docker)

uid is uw PUID en gid is uw PGID. Voor een script printen id -u en id -g enkel de getallen. Op de meeste nieuwe VPS-images is het eerste menselijke account 1000:1000, maar ga hier niet zomaar vanuit. Een opnieuw geïnstalleerde server, of een tweede account dat later is toegevoegd, geeft 1001 of hoger, en een onjuist getal hier is de volledige oorzaak van de fout. Als uw services draaien onder een toegewezen service-account in plaats van uw eigen inloggebruiker, voer dan id thatuser uit en neem de getallen daarvandaan.

Waarom uw bestanden worden weergegeven als 911:911

ls -l toont een numeriek ID in plaats van een naam wanneer er geen host-account overeenkomt met dat ID. Niets op uw server heeft UID 911, dus er is geen naam om weer te geven. Gebruik ls -ln om altijd nummers te zien en de onduidelijkheid weg te nemen:

ls -ln /srv/appdata/sonarr
drwxr-xr-x 2 911 911 4096 Aug  7 09:12 Backups
-rw-r--r-- 1 911 911  512 Aug  7 09:12 config.xml

Die output geeft aan dat de container is uitgevoerd met de ingebouwde standaardinstellingen. Het bestand config.xml in die lijst is tevens het bestand dat de authenticatie-instellingen bevat. Dit is van belang wanneer u de web-UI voor de eerste keer opent en ontdekt dat Sonarr en Radarr worden geleverd zonder standaardgebruikersnaam of -wachtwoord. Controleer dit vanuit de container in plaats van te gissen:

docker exec sonarr id abc
docker compose logs sonarr | head -n 25

De linuxserver-init toont het resultaat in het opstartlogboek als twee regels:

User UID:    911
User GID:    911

Als die regels 911 aangeven nadat u PUID=1000 in uw Compose-bestand heeft ingesteld, heeft de variabele de container nooit bereikt. De gebruikelijke oorzaak is dat u docker-compose.yml heeft bewerkt en vervolgens docker compose restart heeft uitgevoerd, waardoor de bestaande container met de oorspronkelijke omgeving wordt hergebruikt. Omgevingswijzigingen vereisen docker compose up -d, wat de container opnieuw aanmaakt.

Waarom u een bestand dat door de container is geschreven niet kunt verwijderen

De kernel vergelijkt getallen, nooit namen. Uw shell draait als UID 1000. Het bestand is eigendom van UID 911. De map waarin het staat is drwxr-xr-x en is eveneens eigendom van 911, waardoor de groep en anderen wel lees- en uitvoerrechten hebben, maar geen schrijfrechten. Voor het verwijderen van een bestand is schrijftoegang tot de map vereist, niet tot het bestand zelf. Daarom krijgt u deze foutmelding, zelfs als het bestand er onschadelijk uitziet:

rm: cannot remove '/srv/appdata/sonarr/config.xml': Permission denied

Een schrijvende container loopt aan de andere kant tegen hetzelfde probleem aan. Als de hostmap eigendom is van uw gebruiker met modus 755 en de applicatie draait als 911, mislukt de eerste schrijfactie met Permission denied en rapporteert de applicatie dit in eigen bewoordingen. In een .NET-applicatie zoals Sonarr of Radarr verschijnt dit als UnauthorizedAccessException: Access to the path '/data/downloads' is denied. De rechtenreeks voor het bestand geeft aan door welke van de drie rechtenreeksen u daadwerkelijk wordt beoordeeld, en het correct lezen van drwxr-xr-x verandert die foutmelding van mysterieus in overduidelijk.

Dit is specifiek een probleem met bind mounts. Wanneer Docker een lege named volume aanmaakt en deze koppelt over een pad dat al in de image bestaat, kopieert het de inhoud van dat pad naar de volume, inclusief eigenaarschap en rechten, zodat de applicatie een map vindt die al van de applicatie is. Een bind mount krijgt die behandeling niet: Docker koppelt uw hostmap precies zoals deze is. Dat verschil is een van de praktische redenen om te weten wanneer een bind mount de voorkeur verdient boven een named volume en wanneer niet.

Een map corrigeren die reeds onjuist is

Het instellen van PUID en PGID wijzigt het gedrag van de applicatie vanaf dat moment. Het herstelt niet met terugwerkende kracht de bestanden die al op de schijf staan. Stop de stack, corrigeer zelf de eigendomsrechten en start de stack vervolgens opnieuw:

docker compose down
sudo chown -R 1000:1000 /srv/appdata/sonarr
docker compose up -d

Gebruik sudo chown -R "$(id -u):$(id -g)" /srv/appdata/sonarr als u de getallen liever niet handmatig typt. Voer dit uit terwijl de container is gestopt, omdat een actieve applicatie die midden in een schrijfproces zit tijdens een recursieve chown kan eindigen met een gedeeltelijk gecorrigeerde mappenstructuur en een verwarrende tweede reeks foutmeldingen.

Wat PUID en PGID niet oplossen

Dit is het punt waar gebruikers die alles correct hebben ingesteld, vaak vastlopen. Het linuxserver-init-script voert bij het opstarten een chown uit op precies drie paden: /app, /config en /defaults. Uw media-mounts staan niet op die lijst. /data, /downloads en /tv worden ongewijzigd aan de applicatie doorgegeven. Als de host-zijde van deze mounts eigendomsrechten heeft waar de container-gebruiker niet naar kan schrijven, start de container probleemloos op, toont de juiste UID in de banner, maar faalt vervolgens bij de eerste import.

Dit is het juiste gedrag. Een recursieve chown over een mediabibliotheek van twaalf terabyte bij elke start van de container zou een ramp zijn. Dit betekent wel dat het beheer van de media-mappen uw verantwoordelijkheid is; dit zijn de mounts waar rechten daadwerkelijk fout kunnen gaan. Omdat dit type fout pas uren nadat de container gezond leek in een applicatielogboek verschijnt, is een periodieke schrijftest gekoppeld aan ntfy op uw eigen VPS, die meldingen naar uw telefoon stuurt een eenvoudige manier om hiervan op de hoogte te blijven voordat u er na een week achter komt dat er afleveringen ontbreken.

Drie manieren om de gebruiker te beheren en wanneer u welke gebruikt

PUID en PGID omgevingsvariabelen

Dit werkt alleen bij images waarvan het entrypoint deze variabelen uitleest. Het is een populaire methode omdat de container nog steeds als root start, zelf de configuratie uitvoert, /config herstelt en pas daarna de rechten verlaagt. Docker Mods en aangepaste init-scripts blijven werken. Het nadeel is dat u vertrouwt op een conventie in plaats van op een platformfunctie, en de namen van de variabelen zijn niet consistent over verschillende projecten heen.

De user: sleutel in Compose

Dit is een echte Docker-functie die op elke image werkt, omdat de container-runtime deze toepast voordat de code van de image zelf wordt uitgevoerd:

services:
  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    user: "1000:1000"

Het proces draait nooit als root, zelfs niet voor een moment, wat een echte beveiligingswinst oplevert. Het breekt echter alles in het entrypoint dat root-rechten vereiste. Bij linuxserver-images ondersteunt het project dit op basis van 'redelijke inspanning' en alleen voor images die zij hebben getest. De voorbehouden zijn specifiek: PUID en PGID hebben geen effect meer, Docker Mods worden niet uitgevoerd, aangepaste services starten niet en u bent zelf verantwoordelijk voor de rechten op elke gemounte volume. Hun gedocumenteerde patroon combineert de vlag met een beschrijfbare /run:

user: 1000:1000
tmpfs:
  - /run:uid=1000,gid=1000,exec
security_opt:
  - no-new-privileges=true

Eén cosmetisch bijeffect verrast gebruikers vaak. Een numerieke user: heeft geen overeenkomend item in de /etc/passwd van de container, waardoor tools binnenin whoami: cannot find name for user ID 1000 rapporteren. Het ID is geldig en bestandstoegang werkt normaal. Alleen het opzoeken van de naam mislukt.

Rootless Docker

Rootless Docker voert de daemon zelf uit als uw niet-bevoorrechte gebruiker, waardoor er niets op de server als echte root draait. Dit verandert de rekenwijze van het eigendom volledig. Container UID 0 wordt gemapt naar de host-UID van de gebruiker die Rootless Docker uitvoert, en container UID n voor elke n van 1 of hoger wordt gemapt naar subuid + (n - 1), waarbij subuid de basis is van het bereik dat aan u is toegewezen in /etc/subuid en /etc/subgid. Docker verwacht daar ten minste 65.536 ondergeschikte ID's.

Lees die mapping nog eens goed door, want het draait het gebruikelijke advies om. Bij Rootless Docker produceert een container die als root schrijft bestanden die van u zijn. Een container die schrijft als UID 1000 produceert bestanden die eigendom zijn van een ondergeschikt ID rond de 100999, waar uw shell niet bij kan. De PUID-waarde die correct is op een rootful daemon, is hier dus de verkeerde. De twee mechanismen lossen hetzelfde probleem op in verschillende lagen, en ze combineren zonder controle is de reden waarom mensen eindigen met een map waarvoor ze sudo nodig hebben om deze te verwijderen. Als u Rootless gaat gebruiken, test dan het eigendom van één geschreven bestand op uw eigen server voordat u een bibliotheek migreert.

Voor de meeste self-hosted stacks op een enkele VPS is PUID en PGID op een rootful daemon de pragmatische keuze, omdat de images daarvoor zijn gebouwd en gedocumenteerd. Kies voor user: wanneer de README van de image aangeeft dat deze daarvoor is getest, of wanneer u een officiële upstream-image gebruikt die helemaal geen PUID-ondersteuning heeft. Een document-workspace zoals een self-hosted AFFiNE-instantie op één VPS valt in die laatste categorie, omdat geen van de containers PUID uitleest en het eigendom van de databasemap en geüploade bestanden wordt bepaald door de runtime in plaats van door iets in het omgevingsblok. Hetzelfde geldt voor een self-hosted Chatwoot support desk, waar de Rails-container en de Sidekiq-worker beide naar één uploadmap schrijven en geen van beide PUID uitleest; die map moet dus overeenkomen met de gebruiker waarmee de image standaard draait. Niets daarvan verandert voor een nieuwere stack, dus elke persoon in het team een eigen gesandboxte OneCLI-agent geven laat de workspace-mappen per persoon en de Postgres-datamap eigendom zijn van de gebruiker waarmee elke image standaard draait, wat het een user: en chown probleem maakt in plaats van een PUID-probleem. Plaats een enkele self-hosted API voor Codex, Claude Code en Hermes en u erft dezelfde opzet, omdat die image draait als zijn eigen ingebouwde gebruiker en de bind mount die de database en opgeslagen sleutels bevat, het eigendom overneemt dat die gebruiker meebrengt.

De mediastack-case: één groep gedeeld over containers

Een arr-mediastack met Sonarr, Radarr en een downloadclient is waar dit ophoudt theorie te zijn. De downloadclient schrijft een voltooid bestand naar /data/downloads. Sonarr maakt vervolgens een hardlink of verplaatst dat bestand naar /data/media. Om de hardlink te laten werken, moeten beide containers schrijftoegang hebben tot dezelfde boomstructuur. Als de downloadclient draait als 1000 en Sonarr als 1001, bezit een van hen bestanden die de ander alleen kan lezen.

De oplossing is een gedeelde groep die elke container in de stack gebruikt als zijn PGID:

sudo groupadd -g 13000 media
sudo usermod -aG media deploy
sudo chown -R deploy:media /srv/media
sudo find /srv/media -type d -exec chmod 2775 {} +
sudo find /srv/media -type f -exec chmod 0664 {} +

De voorloop-2 in 2775 is de setgid-bit. Op een map betekent dit dat elk nieuw bestand en elke submap die erin wordt aangemaakt, de groep media erft in plaats van de primaire groep van de maker. Hierdoor blijft de configuratie behouden bij nieuwe downloads zonder dat u opnieuw chown hoeft uit te voeren. Log uit en weer in, of voer newgrp media uit, voordat u uw eigen toegang controleert: een groep die is toegevoegd met usermod -aG verschijnt niet in een reeds geopende shell-sessie.

Binnen de container hernummert groupmod -o -g 13000 abc de abc-groep naar 13000, zodat abc schrijft met dezelfde GID als uw media-groep op de host. Elke container in de stack behoudt zijn eigen PUID en deelt die ene PGID. Dit geldt ook voor de containers verderop in de keten die de voltooide bibliotheek alleen lezen, zoals Jellyfin zelf en de front-ends die mensen eraan koppelen, zoals Halcyon, dat die bibliotheek presenteert als een doorzoekbare videotheek uit de jaren 90.

Stel vervolgens UMASK=002 in op elke linuxserver-container in de stack. Dit is de stap die mensen vaak vergeten. De standaardwaarde in deze images is UMASK=022, wat de groepsschrijfbit van elk nieuw bestand verwijdert. Hierdoor krijgen bestanden de rechten 0644 en doet de zojuist geconfigureerde deling niets. 002 resulteert in 0664-bestanden en 0775-mappen, waardoor de groep kan schrijven:

services:
  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - UMASK=002
      - TZ=Etc/UTC
    volumes:
      - /srv/appdata/sonarr:/config
      - /srv/media:/data
    restart: unless-stopped

Deze twee waarden horen thuis in een .env-bestand naast het Compose-bestand, zodat de gehele stack één definitie leest:

PUID=1000
PGID=13000

Compose leest dat bestand automatisch voor ${PUID}-stijl substitutie; dit is hetzelfde mechanisme dat u gebruikt voor inloggegevens. De gewoontes rondom het buiten docker-compose.yml houden van waarden en deze in een .env-bestand plaatsen zijn hier ook van toepassing, met het verschil dat deze twee getallen niet geheim zijn.

Controleer het van begin tot eind in plaats van op de configuratie te vertrouwen. Schrijf een bestand vanuit één container en lees het vanaf de host:

docker exec sonarr touch /data/downloads/permtest
ls -ln /srv/media/downloads/permtest

Een correct resultaat toont uw PUID als eigenaar, 13000 als groep en -rw-rw-r-- als modus. Als de groep 1000 leest, ontbreekt de setgid-bit op die map. Als de modus -rw-r--r-- leest, is de UMASK-variabele niet effectief geworden; controleer of u de container opnieuw hebt aangemaakt in plaats van alleen herstart. Verwijder het testbestand met rm /srv/media/downloads/permtest wanneer u klaar bent.

Welke images gebruiken welke variabele

Images van linuxserver.io gebruiken PUID, PGID en UMASK. Paperless-ngx gebruikt andere namen voor hetzelfde concept: USERMAP_UID en USERMAP_GID, die beide standaard op 1000 staan, en de documentatie adviseert deze te lezen uit id -u en id -g. Fotoservers vertonen dezelfde variatie: PhotoPrism heeft zijn eigen PHOTOPRISM_UID en PHOTOPRISM_GID-paar, terwijl Immich geen equivalent meelevert en de containergebruiker overlaat aan de user:-sleutel van Docker. Daarom bepaalt de keuze tussen PhotoPrism en Immich ook welk van deze mechanismen u zult onderhouden voor de grootste bibliotheek op de server. Veel officiële upstream-images, waaronder de gangbare database- en webserver-images, leveren een vaste ingebouwde gebruiker mee en verwachten dat u user: gebruikt of de instelling ongewijzigd laat. Kleine implementaties met één applicatie roepen dezelfde vraag op. Wanneer u dus een zelfgehoste openGym workout tracker opzet, is het de moeite waard om te controleren onder welke gebruiker de container daadwerkelijk draait voordat u er een bind mount naar verwijst. De map die de database bevat, neemt die eigendom namelijk over, ongeacht of u PUID heeft ingesteld. Een relay voor externe toegang valt in dezelfde categorie. Wanneer u dus uw eigen RustDesk relay-server draait, komt het Ed25519-sleutelpaar dat hbbs bij de eerste start schrijft in uw bind mount terecht met de eigendom van de gebruiker waarmee dat image eindigde, en is een chown aan de hostzijde de enige correctie die u kunt toepassen. Hetzelfde geldt voor infrastructuur die u later toevoegt. Het plaatsen van Authentik voor uw applicaties voor een centrale login betekent dat u officiële server-, Postgres- en Redis-images draait die PUID helemaal niet lezen; het eigendom van hun volumes wordt bepaald door de runtime in plaats van door een entrypoint dat u kunt configureren.

Controleer daarom de README van elk image voordat u een omgevingsblok tussen projecten kopieert. Docker geeft elke omgevingsvariabele die u instelt door aan elke container, ongeacht of iets daarbinnen deze leest. Een PUID die door niets wordt verbruikt, levert geen foutmelding, geen waarschuwing en geen effect op. De container draait als de gebruiker waarmee het eigen Dockerfile eindigde, en u komt hierachter door de eigendom van de bestanden die de container schrijft te controleren. Doe dit onderzoek voordat u iets nieuws aan de server toevoegt, inclusief een zelfgehoste open-kritt security scanning stack, waarbij het Compose-bestand u vertelt of de images PUID ondersteunen of dat het eigendom van de mappen die u mount vastligt in de images zelf.

FAQ

Waarom zijn mijn Docker-bestanden eigendom van 911:911?

911 is de UID en GID van de abc-gebruiker die in linuxserver.io-images is ingebouwd. Als u dit ziet, betekent dit dat de container is gestart zonder dat PUID en PGID zijn ingesteld, waardoor het init-script de ingebouwde standaardwaarden heeft behouden. ls -l toont de ruwe getallen omdat geen enkel account op uw host ID 911 heeft; er is dus geen naam om weer te geven. Stel PUID en PGID in op de uitvoer van id, maak de container opnieuw aan met docker compose up -d en herstel vervolgens de bestaande bestanden met sudo chown -R 1000:1000 op de betreffende map.

Werken PUID en PGID op elke Docker-image?

Nee. Dit is geen Docker-functionaliteit en Docker leest deze variabelen nooit. Ze werken alleen bij images waarvan het entrypoint deze variabelen leest en usermod en groupmod aanroept voordat de applicatie wordt gestart. Dit geldt voor de linuxserver.io-familie en enkele projecten die dit patroon hebben overgenomen. Andere projecten gebruiken andere namen, zoals USERMAP_UID en USERMAP_GID in paperless-ngx. Bij een image die geen van beide ondersteunt, worden de variabelen geaccepteerd en zonder waarschuwing genegeerd.

Moet ik PUID en PGID gebruiken of de user:-sleutel in Docker Compose?

Gebruik PUID en PGID wanneer de image deze ondersteunt, omdat het entrypoint lang genoeg als root draait om /config te herstellen en de eigen services correct te starten. Gebruik user: wanneer de image geen PUID-ondersteuning heeft, of wanneer in de README van de image staat dat deze is getest voor gebruik zonder root-rechten. Bij een linuxserver-image zorgt het instellen van user: ervoor dat PUID en PGID inactief worden, stopt het uitvoeren van Docker Mods en aangepaste services, en wordt u zelf verantwoordelijk voor de rechten van elke gemounte volume.

Sonarr heeft de juiste PUID, maar kan nog steeds geen bestanden verplaatsen. Wat is er mis?

Controleer de volgende drie punten in deze volgorde. Ten eerste de media-mount zelf: het init-script voert alleen een chown uit op /app, /config en /defaults, waardoor /data of /downloads de eigendomsrechten behoudt die het op de host heeft. Ten tweede de gedeelde groep: als de downloadclient en Sonarr onder verschillende GID's draaien, kan geen van beide de bestanden van de ander wijzigen. Geef daarom elke container in de stack dezelfde PGID. Ten derde de umask: de standaardwaarde UMASK=022 van de image schrijft bestanden als 0644 zonder schrijfrechten voor de groep, wat een gedeelde groep volledig nutteloos maakt. Stel UMASK=002 in en zet de setgid-bit op de mappen met chmod 2775, zodat nieuwe bestanden de groep overerven.