Docker · Dateirechte · UID/GID · Sicherheit · DevOps
Docker Dateirechte zwischen Host und Container
sauber und dauerhaft lösen

Dateien die im Container als root erstellt wurden und auf dem Host nicht bearbeitbar sind, Volume-Mounts die Lese- oder Schreibzugriff verweigern, Prozesse die mit UID 0 laufen obwohl das nicht nötig wäre – Docker Dateirechte sind eines der häufigsten Probleme in der täglichen Entwicklung und im Produktionsbetrieb. UID/GID-Mapping, fixuid und rootless Docker lösen diese Konflikte systematisch.

14 Min. Lesezeit UID · GID · userns-remap · fixuid · Bind Mounts · Named Volumes Docker Engine 24+ · Linux · rootless Docker

1. Wie Docker Dateirechte und UID/GID-Mapping funktionieren

Docker-Container teilen sich den Kernel des Hosts, aber nicht die Benutzer-Namespace. Das bedeutet: Die numerische UID 1000 im Container ist dieselbe UID 1000 auf dem Host-System – es gibt keine automatische Übersetzung. Wenn ein Prozess im Container als UID 0 (root) läuft und eine Datei auf einem gemounteten Volume schreibt, wird diese Datei auf dem Host ebenfalls als root-owned angelegt. Der lokale Entwickler mit UID 1000 kann sie dann nicht mehr bearbeiten, weil die Dateiberechtigungen 640 oder 600 mit root als Eigentümer gesetzt sind.

Dieses Verhalten ist kein Bug, sondern das korrekte Funktionieren des Linux-Berechtigungssystems. Docker Dateirechte-Probleme entstehen immer dort, wo die UID des Prozesses im Container nicht mit der UID des Benutzers auf dem Host übereinstimmt, der die Dateien öffnen oder bearbeiten möchte. Das tritt besonders bei Bind Mounts auf – wenn also ein Verzeichnis vom Host in den Container gemountet wird. Die Lösung liegt im UID/GID-Mapping: Container-Prozesse müssen mit derselben UID laufen wie der Host-Benutzer, der die Dateien besitzt, oder die Berechtigungen müssen explizit so gesetzt werden, dass alle Parteien schreiben können.

2. Das klassische Berechtigungsproblem mit Bind Mounts

Das typische Szenario: Ein Entwickler mountet sein lokales Projektverzeichnis als Bind Mount in einen PHP- oder Node-Container. Im Container wird ein Build oder ein Installationsschritt ausgeführt, der neue Dateien erzeugt. Die neuen Dateien gehören der UID des Prozesses im Container – häufig UID 0 (root) oder einer anderen festen UID, die im Dockerfile mit USER gesetzt wurde. Danach kann der lokale Entwickler diese Dateien nicht mehr mit seinem Editor bearbeiten oder per git committen, weil die Docker Dateirechte ihn ausschließen.

Das Problem verschärft sich, wenn CI/CD-Pipelines dasselbe Image verwenden. In der Pipeline läuft der Container als UID 0, der Build erzeugt Dateien als root, und die anschließende Stage, die als unprivilegierter Benutzer läuft, kann die Dateien nicht lesen. Solche Docker Dateirechte-Fehler in CI sind schwer zu debuggen, weil sie umgebungsabhängig sind und lokal oft nicht auftreten – dort läuft der Container ebenfalls als root. Die Grundursache ist immer dieselbe: die UID des Container-Prozesses stimmt nicht mit der UID des erwarteten Dateieigentümers überein.


# Diagnose: check which UID a container process runs as
docker run --rm nginx id
# uid=0(root) gid=0(root) — runs as root, will create root-owned files

# Check file ownership after writing to a bind mount
docker run --rm -v "$(pwd):/workspace" node:20 \
  sh -c "npm install"
ls -la node_modules/
# drwxr-xr-x root root node_modules/   <- owned by root, not by your user

# Quick fix: explicitly pass host UID/GID to the container process
docker run --rm \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd):/workspace" \
  -w /workspace \
  node:20 npm install
ls -la node_modules/
# drwxr-xr-x 1001 1001 node_modules/   <- owned by your host user

3. Das --user-Flag: einfachste Lösung für viele Fälle

Das --user-Flag beim docker run-Aufruf ist die direkteste Methode, um Docker Dateirechte-Probleme zu beheben. Mit --user $(id -u):$(id -g) läuft der Container-Prozess mit der UID und GID des aktuell eingeloggten Host-Benutzers. Dateien, die der Prozess schreibt, gehören dann dem Host-Benutzer und sind für ihn direkt zugänglich. In Docker Compose kann dasselbe mit der user-Direktive erreicht werden, entweder als statischer Wert oder als Umgebungsvariable.

Das --user-Flag hat jedoch eine wichtige Einschränkung: Der Benutzer mit der angegebenen UID muss im Container existieren oder die Anwendung darf keine Benutzer-spezifischen Konfigurationen (Home-Verzeichnis, Passwortdatei) benötigen. Viele Anwendungen schreiben beim Start in ihr Home-Verzeichnis oder lesen aus /etc/passwd. Wenn die UID nicht in /etc/passwd eingetragen ist, schlagen diese Operationen fehl. Das ist der Grund, warum das Flag allein nicht immer ausreicht und Ansätze wie fixuid notwendig werden.

4. Dockerfile: Benutzer und Rechte von Anfang an richtig setzen

Die sauberste Grundlage für korrekte Docker Dateirechte entsteht bereits im Dockerfile. Ein dedizierter nicht-root-Benutzer wird mit RUN addgroup und adduser angelegt, bekommt Zugriff auf alle Anwendungsverzeichnisse und wird als Standardbenutzer für den Container-Prozess gesetzt. Das USER-Statement im Dockerfile sorgt dafür, dass alle nachfolgenden RUN-, CMD- und ENTRYPOINT-Befehle mit diesem Benutzer ausgeführt werden. In Multi-Stage-Builds wird der nicht-root-Benutzer üblicherweise in der finalen Stage definiert, nachdem alle Build-Schritte, die root-Zugriff benötigen, abgeschlossen sind.

Eine häufige Falle beim Thema Docker Dateirechte im Dockerfile: Verzeichnisse werden als root erstellt und dann der Benutzer gewechselt, ohne die Eigentümerschaft der Verzeichnisse zu übertragen. Der Container-Prozess kann dann nicht in seine eigenen Arbeitsverzeichnisse schreiben. Das korrekte Muster ist: COPY --chown=user:group und RUN mkdir ... && chown user:group dir immer vor dem USER-Statement ausführen, solange der Build noch als root läuft.


# Dockerfile — proper user setup for correct file permissions
FROM php:8.4-fpm-alpine

# Create application user with fixed UID/GID matching typical host user
RUN addgroup -g 1000 -S appgroup && \
    adduser -u 1000 -S appuser -G appgroup

# Install dependencies as root before switching user
RUN apk add --no-cache git

WORKDIR /var/www/html

# Copy files and set ownership in a single layer (no extra chown layer)
COPY --chown=appuser:appgroup . .

# Create writable directories and hand them to appuser
RUN mkdir -p var/cache var/log pub/media && \
    chown -R appuser:appgroup var/ pub/

# Switch to non-root user — all subsequent commands run as appuser
USER appuser

# Verify: image must never run as root in production
# docker run --rm myimage id  ->  uid=1000(appuser) gid=1000(appgroup)

5. fixuid: dynamische UID/GID-Anpassung im Container

fixuid ist ein kleines Go-Programm, das beim Container-Start die UID und GID des Container-Benutzers auf eine extern übergebene UID/GID umschreibt. Das Ergebnis: Der Container-Prozess läuft mit der UID des Host-Benutzers, ohne dass das Image für jeden Entwickler neu gebaut werden muss. fixuid wird als Entrypoint-Wrapper eingesetzt und liest die gewünschte UID aus der FIXUID-Umgebungsvariable oder aus dem --user-Flag.

Dieser Ansatz ist besonders für Team-Entwicklungsumgebungen wertvoll, wo verschiedene Entwickler unterschiedliche UIDs haben. Mit fixuid kann jeder Entwickler dasselbe Docker-Image verwenden und bekommt trotzdem korrekte Docker Dateirechte auf alle gemounteten Dateien. Die Konfiguration erfolgt in einer config.yml-Datei, die ins Image kopiert wird und die Standard-UID und -GID sowie die Pfade definiert, deren Eigentümerschaft beim Start angepasst werden soll. Im Compose-File wird dann user: "${UID}:${GID}" gesetzt, wobei UID und GID aus der lokalen Shell-Umgebung kommen.

6. Named Volumes vs. Bind Mounts: wann was verwenden

Named Volumes und Bind Mounts verhalten sich grundlegend unterschiedlich bezüglich Docker Dateirechte. Bei einem Bind Mount wird ein Host-Verzeichnis mit allen seinen bestehenden Berechtigungen und Eigentümerschaften in den Container gemountet. Bei einem Named Volume erstellt Docker ein verwaltetes Verzeichnis, das zunächst leer ist und dessen Initialbefüllung beim ersten Container-Start aus dem Image-Layer erfolgt. Named Volumes gehören Docker und werden in einem von Docker verwalteten Bereich auf dem Host gespeichert.

Für Anwendungsdaten, die nur im Container gebraucht werden (Caches, generierte Assets, Datenbank-Dateien), sind Named Volumes die bessere Wahl, weil sie keine Docker Dateirechte-Konflikte mit dem Host-Benutzer erzeugen. Für Quellcode, den der Entwickler lokal bearbeiten und direkt im Container testen möchte, sind Bind Mounts notwendig. In diesem Fall ist explizites UID-Mapping oder fixuid erforderlich. Eine elegante Hybrid-Strategie kombiniert einen Bind Mount für den Quellcode mit Named Volumes für Schreibverzeichnisse wie var/cache oder vendor, die der Container exklusiv verwaltet.

7. userns-remap: systemweite UID-Isolation

User Namespace Remapping (userns-remap) ist eine Docker-Daemon-Konfiguration, die systemweit alle Container-UIDs auf einen verschobenen Bereich des Host-UID-Raums abbildet. Mit "userns-remap": "default" in der Docker-Daemon-Konfiguration wird beispielsweise UID 0 im Container auf UID 100000 auf dem Host gemappt. Das bedeutet: Selbst wenn ein Container-Prozess root-Rechte hat, sind seine Dateien auf dem Host im UID-Bereich 100000-165535 und haben damit keinerlei Zugriff auf Host-Dateien.

userns-remap löst ein wichtiges Sicherheitsproblem mit Docker Dateirechten: Container-Prozesse, die aus ihrem Container ausbrechen, landen auf dem Host als unprivilegierte Benutzer mit hohen UIDs. Für Entwicklungsumgebungen hat userns-remap jedoch einen Nachteil: Bind Mounts verhalten sich unter userns-remap anders, und bestehende Volumes müssen neu initialisiert werden. In CI/CD-Umgebungen oder auf Shared-Hosts, wo viele Teams dieselbe Docker-Engine teilen, ist userns-remap eine wichtige Sicherheitsmaßnahme gegen Privilege-Escalation über Docker-Volumes.


# /etc/docker/daemon.json — enable user namespace remapping
{
  "userns-remap": "default"
}

# After daemon restart: verify remapping is active
docker info | grep "Security Options"
# Security Options:
#  userns

# Check mapped UIDs for a running container
cat /etc/subuid  # shows the allocated range, e.g.: dockremap:100000:65536

# Named volume ownership under userns-remap
# Docker automatically adjusts volume ownership when userns-remap is active
docker volume create myapp_data
docker run --rm -v myapp_data:/data alpine stat /data
# should show uid=0 inside container = uid=100000 on host

# docker-compose.yml: user mapping is handled automatically under userns-remap
# No --user flag needed — root in container is unprivileged on host
services:
  app:
    image: myapp:latest
    volumes:
      - myapp_data:/var/www/html/var
volumes:
  myapp_data:

8. Rootless Docker: der sicherste Ansatz

Rootless Docker ist eine Betriebsart, bei der der Docker-Daemon selbst ohne root-Rechte läuft. Jeder Benutzer kann eine eigene Docker-Instanz betreiben, die vollständig in seinem Benutzer-Namespace isoliert ist. Docker Dateirechte-Probleme mit Bind Mounts entfallen bei rootless Docker weitgehend, weil der Daemon mit der UID des Benutzers läuft und alle Container-Prozesse im Namespace dieses Benutzers operieren. Dateien, die ein Container-Prozess auf einem Bind Mount schreibt, gehören auf dem Host automatisch dem richtigen Benutzer.

Die Installation von rootless Docker erfordert einige Systemvoraussetzungen: newuidmap und newgidmap müssen installiert sein, und der Benutzer braucht Sub-UID- und Sub-GID-Einträge in /etc/subuid und /etc/subgid. Rootless Docker hat gegenüber klassischem Docker einige Einschränkungen: Bestimmte Netzwerk-Features, privilegierte Ports unter 1024 und einige Storage-Driver sind nicht verfügbar. Für die meisten Entwicklungs- und Produktions-Workloads sind diese Einschränkungen jedoch irrelevant, und rootless Docker bietet die sauberste Lösung für Docker Dateirechte-Probleme.

9. Ansätze im direkten Vergleich

Für Docker Dateirechte-Probleme gibt es mehrere Lösungsansätze, die sich in Aufwand, Sicherheit und Eignung für verschiedene Szenarien unterscheiden.

Ansatz Aufwand Sicherheit Empfehlung
--user $(id -u) Minimal Gut Einfache Entwicklungssetups
Dockerfile USER + chown Gering Sehr gut Produktion: immer
fixuid Mittel Gut Team-Entwicklung, diverse UIDs
Named Volumes Gering Sehr gut Interne Container-Daten
userns-remap Hoch Maximal Shared Hosts, Security-kritisch
Rootless Docker Mittel Maximal Neue Setups, CI/CD

Die Empfehlung für die meisten Teams: Im Dockerfile immer einen nicht-root-Benutzer mit fester UID definieren und Named Volumes für alle Container-internen Schreibverzeichnisse verwenden. Nur für Quellcode-Bind-Mounts in der Entwicklung ist zusätzlich das --user-Flag oder fixuid nötig. Rootless Docker ist die langfristig sauberste Lösung und wird aktiv von Docker Inc. und Red Hat (Podman) gefördert.

Mironsoft

Docker-Sicherheit, Dateirechte-Architektur und sichere CI/CD-Setups

Docker Dateirechte dauerhaft im Griff?

Wir analysieren bestehende Docker-Setups auf Dateirechte-Probleme, implementieren sichere UID/GID-Strategien und rüsten bestehende Dockerfiles und Compose-Konfigurationen mit korrekten Benutzer-Konfigurationen nach.

Dockerfile-Audit

Analyse auf root-Prozesse, falsche Berechtigungen und unsichere Volume-Konfigurationen

UID/GID-Strategie

fixuid-Integration oder rootless Docker für Team-Entwicklungsumgebungen

Produktionshärtung

userns-remap, read-only Filesystems und Security-Scanning in CI-Pipeline integrieren

10. Zusammenfassung

Docker Dateirechte-Probleme entstehen, wenn die UID des Container-Prozesses nicht mit der UID des erwarteten Datei-Eigentümers auf dem Host übereinstimmt. Die grundlegendste Maßnahme ist, im Dockerfile immer einen nicht-root-Benutzer mit USER zu definieren und alle Anwendungsverzeichnisse mit chown vor dem Benutzerwechsel zu übergeben. Für Entwicklungsumgebungen mit Bind Mounts löst das --user $(id -u):$(id -g)-Flag oder fixuid das Problem, ohne das Image für jeden Entwickler anpassen zu müssen.

Named Volumes sind für alle Container-internen Schreibverzeichnisse vorzuziehen, weil sie keine Host-seitigen Docker Dateirechte-Konflikte erzeugen. Rootless Docker und userns-remap sind die sichersten Ansätze für Produktions- und Multi-Tenant-Umgebungen, weil sie sicherstellen, dass Container-Prozesse auf dem Host niemals mit echten privilegierten UIDs operieren. Die Kombination aus korrekten Dockerfiles, intelligenten Volume-Strategien und UID-Mapping deckt alle häufigen Berechtigungsprobleme ab.

Docker Dateirechte — Das Wichtigste auf einen Blick

Dockerfile-Basis

Immer USER mit fester UID setzen, Verzeichnisse vor USER-Wechsel per chown übergeben. Keine root-Prozesse in Produktion.

Entwicklungsumgebung

--user $(id -u):$(id -g) oder fixuid für Bind Mounts. Named Volumes für vendor, cache und generierte Dateien.

Named vs. Bind Mounts

Named Volumes für Container-interne Daten, Bind Mounts nur für Quellcode der lokal bearbeitet wird. Hybrid-Strategie kombiniert beide.

Produktionssicherheit

Rootless Docker oder userns-remap auf Produktionsservern. Verhindert Privilege-Escalation auch wenn Container-Breakout erfolgt.

11. FAQ: Docker Dateirechte zwischen Host und Container

1Warum gehören Container-Dateien root auf dem Host?
Host und Container teilen den Kernel-UID-Namespace. UID 0 im Container ist UID 0 auf dem Host. --user $(id -u) löst das Problem direkt.
2Named Volume vs. Bind Mount bei Dateirechten?
Bind Mounts spiegeln Host-Berechtigungen. Named Volumes werden von Docker verwaltet, keine Host-seitigen Konflikte. Für interne Container-Daten Named Volumes bevorzugen.
3Was macht fixuid?
Passt UID des Container-Benutzers beim Start dynamisch an die übergebene UID an. Ideal für Teams mit unterschiedlichen Host-UIDs die dasselbe Image nutzen.
4Nicht-root-Benutzer im Dockerfile korrekt aufsetzen?
adduser mit fester UID 1000 anlegen. Verzeichnisse per chown übergeben vor USER-Statement. COPY --chown verwenden. USER ans Ende des Build-Prozesses.
5Was ist rootless Docker?
Docker-Daemon läuft ohne root im Namespace des Benutzers. Bind-Mount-Dateien gehören automatisch dem richtigen Host-Benutzer. Sauberste Langzeit-Lösung.
6userns-remap vs. rootless Docker?
userns-remap: Daemon läuft als root, Container-UIDs werden auf hohe Host-UIDs remappt. Rootless: Daemon läuft komplett ohne root. Beide verhindern Privilege-Escalation, rootless bietet stärkere Isolation.
7Root für Installation, danach user?
Multi-Stage-Build: Build-Stage als root installieren. Finale Stage nur Artefakte kopieren, chown ausführen, USER setzen. Produktionsprozess läuft nie als root.
8Welcher Benutzer läuft in einem Container?
docker exec CONTAINER id zeigt UID/GID. docker run --rm IMAGE id zeigt Default-Benutzer. trivy oder docker scout für Security-Scanning auf root-Prozesse.
9--user schlägt fehl obwohl UID stimmt?
Anwendung sucht UID in /etc/passwd oder HOME. Wenn UID nicht eingetragen, schlagen Lookups fehl. fixuid oder libnss-wrapper dynamisch ergänzen.
10Dateirechte in CI/CD absichern?
trivy und hadolint in CI für root-Prozess-Scanning. USER explizit setzen. Named Volumes für Build-Cache. Artefakte als korrekter Benutzer erzeugen.