Read-only Container-Filesystem - und wo man dann doch schreiben muss
Read-only Container-Filesystem - und wo man dann doch schreiben muss
Ein Read-only-Filesystem zwingt zur expliziten Entscheidung, wo Daten persistiert werden — und reduziert nebenbei die Angriffsfläche.
Das Grundprinzip
docker run --read-only nginx
Der Container kann jetzt nichts mehr in sein Filesystem schreiben. Jeder Schreibversuch schlägt fehl:
docker run --read-only alpine sh -c 'echo test > /test'
# sh: can't create /test: Read-only file system
Warum das sinnvoll ist
- Integrität: Schadsoftware kann keine Dateien in den Container schreiben
- Klarheit: Wo persistente Daten landen, ist explizit konfiguriert (Volumes)
- 12-Factor: Zwingt dazu, Logs über stdout/stderr auszugeben, nicht in Dateien
- Debuggierbarkeit: Der Container-Zustand ist deterministisch — nur Volumes ändern sich
Das Problem: Manche Apps brauchen doch Schreibzugriff
Viele Anwendungen schreiben temporäre Dateien in /tmp, PID-Files nach /run, oder Sockets. Mit --read-only schlagen diese sofort fehl.
Lösung: tmpfs für temporäre Verzeichnisse
docker run --read-only \
--tmpfs /tmp \
--tmpfs /run \
--tmpfs /var/run \
nginx
tmpfs ist RAM-basierter Speicher — nach Container-Neustart leer, aber während der Laufzeit beschreibbar.
In Docker Compose
services:
app:
image: myapp
read_only: true
tmpfs:
- /tmp
- /run
volumes:
- app-data:/app/data # Persistente Daten auf Volume
volumes:
app-data:
Nginx als Beispiel
Nginx schreibt PID-Dateien und temporäre Dateien:
services:
nginx:
image: nginx:alpine
read_only: true
tmpfs:
- /var/cache/nginx
- /var/run
- /tmp
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./html:/usr/share/nginx/html:ro
Was wohin gehört
| Datentyp | Ziel |
|---|---|
| Temporäre Dateien | tmpfs |
| Persistente Anwendungsdaten | Named Volume |
| Logs | stdout/stderr (kein File-Logging) |
| Konfiguration | Volume mit :ro (read-only) |
| Uploads/Dateien | Named Volume oder Objekt-Storage |
Konfigurationsdateien immer read-only mounten:
volumes:
- ./config.yml:/app/config.yml:ro # :ro verhindert versehentliches Überschreiben
Identifizieren was schreibt
Vor der Umstellung testen, welche Pfade die App beschreibt:
# Container starten und Datei-Zugriffe beobachten
docker run --rm -it myapp strace -e trace=openat,open sh -c './start.sh'
# Oder einfach: read-only starten und Fehler beobachten
docker run --read-only myapp 2>&1 | grep -i "read-only"
tmpfs-Optionen
tmpfs:
- /tmp:size=100m,mode=1777 # 100MB, Sticky-Bit für /tmp
- /run:size=10m
Read-only für Volumes
Read-only-Container mit read-only Volumes für maximale Sicherheit:
services:
app:
image: myapp
read_only: true
volumes:
- static-files:/app/static:ro # Nur lesen
- app-data:/app/data # Lesen und schreiben
tmpfs:
- /tmp
volumes:
static-files:
app-data:
Read-only-Filesysteme sind kein Aufwand für paranoia — sie sind eine saubere Architektur-Entscheidung, die Laufzeitumgebung und persistente Daten klar trennt.