Container laufen selten wegen „Docker an sich“ langsam oder instabil – sehr häufig ist das darunterliegende Storage-Setup der eigentliche Auslöser. Dieses Container-Storage-Troubleshooting konzentriert sich auf drei klassische Problemfelder im Betrieb: OverlayFS (meist als overlay2 genutzt), device-mapper (devicemapper/thin provisioning) und die besonders tückischen loopback-Konfigurationen. Ziel ist ein Runbook, das Sie auch unter Zeitdruck nutzen können: Symptome einordnen, Ursachen eingrenzen, mit belastbaren Prüfschritten verifizieren und Änderungen so umsetzen, dass ein Rückfallpfad vorhanden ist.
Wichtig: Viele Storage-Probleme wirken zunächst wie „App-Fehler“ (Timeouts, 500er, Build bricht ab), sind aber in Wahrheit I/O-Latenz, Metadaten-Engpässe oder eine fehlerhafte Treiber-/Filesystem-Kombination. Wer das sauber trennt, spart Stunden in Incident-Calls.
Wie Container-Storage intern funktioniert (Kurzüberblick für die Fehlersuche)
Container-Engines wie Docker speichern Images und Container-Schreibschichten in einem Storage-Backend. Der Storage-Driver entscheidet, wie „Layer“ (nur-lesbare Image-Schichten) und die schreibbare Container-Schicht umgesetzt werden. Bei Linux ist overlay2 (OverlayFS) heute Standard; ältere Setups nutzen teils devicemapper. Zusätzlich gibt es Volumes (persistente Daten, meist als Host-Pfad oder über Volume-Plugins), die unabhängig vom Image-Layering sind.
Für das Troubleshooting ist die Unterscheidung zentral:
- Image-/Container-Layer (Storage-Driver): Builds, Pull/Push, Startzeiten, „Copy-on-write“.
- Volumes/Bind-Mounts: Datenbanken, Uploads, Queue-Daten, Log-Verzeichnisse – oft der Performance-Hotspot.
- Filesystem & Blockdevice: XFS/ext4, LVM, RAID, SAN, virtio, NVMe – hier entstehen Latenzspitzen und Metadaten-Limits.
Symptome: Woran Sie Storage-Probleme früh erkennen
Typische Warnsignale im Container-Betrieb sind erstaunlich wiederholbar. Achten Sie besonders auf diese Muster:
- „no space left on device“, obwohl df -h noch Platz zeigt (häufig Inodes, Thin-Pool voll, oder Overlay-Metadaten).
- Extrem langsame Builds bei vielen kleinen Dateien (Metadaten-Operationen, Copy-up-Kosten bei OverlayFS).
- Container starten verzögert oder hängen bei I/O-intensiven Init-Schritten.
- Hohe iowait auf dem Host, begleitet von Zeitüberschreitungen in Anwendungen.
- Unklare Fehler im Docker-Daemon-Log rund um Mounts, „failed to mount overlay“, „Invalid argument“.
- Plötzlicher Performance-Einbruch nach Kernel-/Distribution-Upgrade oder Filesystem-Migration (z. B. XFS-Optionen, d_type/ftype).
Ersteinschätzung: Welche Storage-Konfiguration läuft überhaupt?
Bevor Sie Details debuggen, ziehen Sie eine Baseline: Storage-Driver, Root-Dir, Filesystem, Mount-Optionen und ob Volumes oder Layer betroffen sind.
Docker-Info und Pfade prüfen
docker info --format 'Driver={{.Driver}}
DockerRootDir={{.DockerRootDir}}
BackingFilesystem={{.BackingFilesystem}}
SupportsDType={{.DriverStatus}}'
docker info | sed -n '/Storage Driver/,$p' | sed -n '1,80p'Warum das hilft: Der Storage-Driver grenzt die Problemklasse ein. DockerRootDir zeigt, wo die Daten liegen (Standard: /var/lib/docker). Das Filesystem darunter entscheidet, ob OverlayFS sauber funktioniert.
Filesystem und Mount-Optionen am DockerRootDir
DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}')
df -Th "$DOCKER_DIR"
findmnt -no SOURCE,FSTYPE,OPTIONS -T "$DOCKER_DIR"
# Inodes: wichtig bei "no space left" trotz freiem Speicher
df -ih "$DOCKER_DIR"Interpretation: Bei OverlayFS sind XFS oder ext4 üblich. XFS muss dabei d_type unterstützen (bei Docker historisch als ftype=1 sichtbar). Inodes sind bei vielen kleinen Dateien und Layern häufig der echte Engpass.
OverlayFS/overlay2: Häufige Ursachen, Prüfschritte, Fixes
OverlayFS ist ein Kernel-Mechanismus, der zwei Verzeichnisebenen „übereinanderlegt“: eine lowerdir-Kette (Image-Layer, nur-lesbar) und ein upperdir (Container-Schreibschicht). Wenn ein Container eine Datei aus einem unteren Layer verändert, passiert ein Copy-up: Die Datei wird in den oberen Layer kopiert und dort geändert. Das ist funktional robust, hat aber klare Performance- und Metadaten-Charakteristiken.
Stolperfalle 1: XFS ohne d_type (ftype=0) bzw. „Invalid argument“
Ein Klassiker: Docker startet, aber bei bestimmten Operationen erscheinen Mount-Fehler oder Container-Starts scheitern. Hintergrund: OverlayFS benötigt d_type (Directory Entry Type), um Dateitypen zuverlässig zu erkennen. Bei XFS ist das eine Formatierungsoption; bei ext4 ist es in der Praxis meist unkritisch.
# Nur sinnvoll, wenn das Backing-FS XFS ist:
# Ausgabe: ftype=1 ist gut; ftype=0 ist problematisch.
XFS_DEV=$(findmnt -no SOURCE -T "$(docker info --format '{{.DockerRootDir}}')")
# xfs_info erwartet das Blockdevice oder den Mountpoint
xfs_info "$(findmnt -no TARGET -T "$(docker info --format '{{.DockerRootDir}}')")" | tr ' ' 'n' | grep -E '^ftype=' || trueFix-Realität: ftype ist nicht „umschaltbar“. Wenn XFS mit ftype=0 formatiert wurde, bleibt nur Migration auf ein korrekt formatiertes Filesystem (z. B. neues XFS mit ftype=1 oder ext4) und anschließendes Neuaufsetzen des Docker-Data-Roots. Planen Sie dafür zwingend Downtime und ein Image-Repull ein (siehe Abschnitt „Migration und Rückfallstrategie“).
Stolperfalle 2: „no space left“ durch Inode- oder Metadaten-Engpässe
Overlay2 erzeugt viele Verzeichnisse und Metadaten pro Layer. Bei CI-Workloads oder Build-Hosts ist Inode-Erschöpfung häufiger als echter Kapazitätsmangel.
DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}')
df -h "$DOCKER_DIR"
df -ih "$DOCKER_DIR"
# Hotspot-Suche: wo liegen besonders viele Einträge?
# (Kann dauern, bei Bedarf außerhalb der Peak-Zeit laufen lassen.)
sudo du -x -d 2 -h "$DOCKER_DIR" 2>/dev/null | sort -h | tail -n 30Warum das scheitert: Auf manchen Filesystemen/Partitionen sind Inodes fix vorgegeben. Selbst wenn 200 GB frei sind, kann ein volles Inode-Kontingent Schreiboperationen hart blockieren. Außerdem können thin-provisioning- oder quota-Mechanismen auf VM-/SAN-Ebene ähnliche Effekte erzeugen.
Stolperfalle 3: Performance-Einbrüche durch Copy-up und „chown -R“ in Images
Viele Admins sehen das Problem zuerst in der Anwendung. Typisch ist aber ein Image/Entrypoint, der beim Start rekursiv Rechte setzt oder große Verzeichnisse verändert. Auf OverlayFS triggert das Copy-ups und Metadaten-Updates – auf vielen kleinen Dateien ist das teuer.
Prüfen Sie, ob der Hotspot in der Container-Schicht liegt oder in Volumes. Ein schneller Indikator ist die I/O-Last beim Start und ob die Pfade innerhalb von Bind-Mounts liegen. In produktiven Setups gilt: Datenverzeichnisse (z. B. Datenbanken) gehören in Volumes, nicht in die Container-Schreibschicht.
OverlayFS-Checks, die im Incident wirklich helfen
# Kernel- und OverlayFS-Sichtbarkeit
uname -r
lsmod | grep -i overlay || true
# Docker-Daemon-Logs (systemd)
sudo journalctl -u docker --since "-2h" | tail -n 200
# Aktive Mounts: Overlay-Mounts zeigen lowerdir/upperdir/workdir
mount | grep -E ' type overlay ' | head -n 20Interpretation: Häufig sehen Sie hier direkt „failed to mount overlay“ oder Kernel-Fehler, die auf inkompatible Optionen, zu lange lowerdir-Ketten (selten heute) oder beschädigte Metadaten hindeuten. Wenn Mounts massiv zunehmen, lohnt auch ein Blick auf Container-Leaks (Container werden nicht entfernt, temporäre Builder laufen voll).
device-mapper/devicemapper: Thin-Pool, Metadaten und Betriebsfallen
device-mapper ist ein Linux-Kernel-Subsystem, das Blockdevices virtualisiert. Im Docker-Kontext ist devicemapper meist als thin provisioning implementiert: Daten und Metadaten werden in einem Thin-Pool verwaltet. Das kann stabil laufen, ist aber empfindlich gegenüber falscher Provisionierung (Pool/Meta voll), Monitoring-Lücken und – ganz besonders – loopback-Setups.
Wichtig: Loopback bei devicemapper ist fast immer ein Anti-Pattern
Bei loopback liegen „virtuelle Blockdevices“ als Dateien auf einem Filesystem. Darauf wird dann wieder devicemapper gebaut. Das addiert Latenz, Cache-Effekte und Failure-Modes. Es ist in Tests ok, im Dauerbetrieb aber eine häufige Ursache für Performance-Probleme, I/O-Spikes und schwer erklärbare „Device is busy“-Fehler.
devicemapper-Status aus Docker heraus lesen
docker info | sed -n '/Storage Driver: devicemapper/,$p' | sed -n '1,120p'Achten Sie hier auf Begriffe wie Pool Name, Data file/Metadata file (Hinweis auf loopback) sowie Deferred Removal/Deferred Deletion.
Thin-Pool-Füllstand und Metadaten prüfen (lvs)
Wenn devicemapper über LVM läuft (häufig), sind LVM-Thin-Pools der Kern. Wichtig ist nicht nur der Datenanteil, sondern auch die Metadaten (Mapping-Tabellen). Metadaten voll bedeutet: Schreibstopp, oft abrupt.
# Übersicht über LVM Thin Pools und deren Auslastung
sudo lvs -a -o +devices,lv_size,data_percent,metadata_percent,segtype,lv_attr,origin,pool_lvInterpretation: data_percent nahe 100% ist kritisch. metadata_percent nahe 100% ist oft noch kritischer, weil dann auch „eigentlich kleine“ Änderungen scheitern. Planen Sie Monitoring/Alerts auf beide Werte.
Direkt über dmsetup prüfen (wenn LVM-Infos nicht reichen)
sudo dmsetup status
sudo dmsetup ls --treeWarum das hilft: dmsetup zeigt auch Zustände, die in Docker nicht sichtbar sind (z. B. deferred deletion Stau). Bei starkem Container-Churn kann „deferred deletion“ die Illusion erzeugen, dass Platz nie frei wird.
Typische Fehlerbilder bei devicemapper
- Pool/Meta voll: Container starten nicht, Pulls scheitern, „no space left“ trotz freier Host-Partition.
- Loopback-Latenz: sporadische Timeouts, hoher iowait, besonders bei Parallel-Builds.
- Unerwartete „Device is busy“: Aufräumoperationen blockieren, wenn Devices noch referenziert sind.
Loopback-Fallstricke gezielt erkennen und bewerten
Loopback ist nicht nur „langsamer“ – es verändert auch die Fehlerdiagnose. Sie haben dann zwei Ebenen, auf denen Platz knapp wird: das Filesystem, das die loopback-Dateien enthält, und den Thin-Pool darüber. Zusätzlich können Fragmentierung und Filesystem-Journaling die Latenz erhöhen.
Ist es wirklich loopback?
docker info | sed -n '/Storage Driver/,$p' | sed -n '1,160p'
# Loop-Devices auf dem Host sichtbar?
losetup -a || true
lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS | sed -n '1,120p'Interpretation: Wenn Sie viele /dev/loop* sehen und Docker devicemapper nutzt, ist die Wahrscheinlichkeit hoch, dass loopback im Spiel ist. Bei overlay2 ist loopback üblicherweise nicht relevant (außer Sie betreiben Docker auf einem loopback-basierten Unterbau, z. B. verschachtelte Lab-Setups).
Entscheidung: Sofortmaßnahme vs. nachhaltige Korrektur
Im Incident ist das Ziel häufig Stabilisierung: Druck aus dem System nehmen, bevor Sie migrieren. Nachhaltig ist aber fast immer: loopback loswerden, Storage-Driver standardisieren und die Docker-Daten auf ein geeignetes Blockdevice legen.
Runbook: Schrittfolge für reproduzierbares Container-Storage-Troubleshooting
Die folgende Prüfsequenz ist so aufgebaut, dass Sie schnell entscheiden können, ob Sie mit Cleanup, Kapazität oder einer strukturellen Migration zu tun haben.
1) Scope klären: Layer-Storage oder Volume-Storage?
# Welche Container schreiben besonders viel? (Grobindikator über Log/IO nicht direkt sichtbar)
# Hilft, Kandidaten zu identifizieren.
docker ps --format 'table {{.Names}}t{{.Image}}t{{.Status}}'
# Volumes und Mounts eines auffälligen Containers anzeigen
C=<container_name_or_id>
docker inspect "$C" --format '{{json .Mounts}}' | jq .Warum das hilft: Wenn ein Problem primär in Volumes liegt (z. B. NFS, CIFS/SMB, iSCSI), bringt Storage-Driver-Tuning wenig. Umgekehrt sind „Image-Pull langsam“ oder „Build hängt“ oft Layer-Storage-Themen.
2) Host-Kapazität, Inodes, Top-Verursacher
DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}')
df -Th "$DOCKER_DIR"
df -ih "$DOCKER_DIR"
# Größte Docker-Verzeichnisse
sudo du -x -d 1 -h "$DOCKER_DIR" 2>/dev/null | sort -h3) Docker-Objekte: Wie viel ist wirklich belegbar?
docker system df
docker system df -v | sed -n '1,200p'Interpretation: Hohe „Reclaimable“-Anteile deuten auf Cleanup-Potenzial. Achtung: „Reclaimable“ ist nicht gleich „gefahrlos“. Löschen kann Build-Caches entfernen und Pull-Last erhöhen.
4) Safe Cleanup (mit klaren Grenzen)
Wenn Sie Speicher kurzfristig freigeben müssen, beginnen Sie konservativ. Planen Sie dabei, dass laufende Container nicht entfernt werden, aber ungenutzte Ressourcen schon.
# Unbenutzte Images, Container (stopped), Netzwerke, Build-Cache entfernen
# Vorsicht in CI-Umgebungen: Build-Cache-Verlust kann Builds verlangsamen.
docker system prune
# Aggressiver: auch unbenutzte Images entfernen
docker system prune -a
# Volumes nur entfernen, wenn Sie absolut sicher sind
docker volume ls
# docker volume pruneWarum das funktioniert: Viele „vollgelaufene“ Hosts sind schlicht Ergebnis fehlender Lifecycle-Regeln. Wann es scheitert: Wenn der Engpass nicht Kapazität ist, sondern Inodes, Thin-Pool-Metadaten oder I/O-Latenz. Oder wenn Platz wegen deferred deletion nicht sofort frei wird (devicemapper).
5) I/O-Latenz sichtbar machen (Host-Perspektive)
Für Storage-Probleme ist nicht nur „%util“ relevant, sondern vor allem Latenz. Je nach Distribution stehen unterschiedliche Tools bereit; iostat ist oft schnell verfügbar.
# Pakete ggf. installieren: sysstat
# sudo apt-get install -y sysstat | sudo yum install -y sysstat
iostat -x 1 10
# Grobe Prozesssicht, um I/O-Wait zu erkennen
vmstat 1 10Interpretation: Hohe await oder svctm (je nach Version) und dauerhaft hohe Auslastung deuten auf Blockdevice-Engpässe hin. Dann sind Cleanup-Maßnahmen oft nur Symptombekämpfung.
Migration: Storage-Driver wechseln oder DockerRootDir verlagern (mit Rückfallstrategie)
Wenn die Diagnose klar ist (z. B. devicemapper loopback im Dauerbetrieb, XFS ohne ftype, dauerhaft zu wenig IOPS), ist eine strukturelle Änderung sauberer als „mehr Platz“. Für Betreiber ist wichtig: Ein Storage-Driver-Wechsel bedeutet in der Regel, dass bestehende lokale Images/Layer im DockerRootDir neu aufgebaut werden müssen. Persistente Daten müssen ohnehin in Volumes liegen; alles andere ist ersetzbar.
Vorbereitung: Was muss gesichert werden?
- Compose-/Stack-Definitionen, systemd-Units, Environment-Dateien: damit Sie Container reproduzierbar starten.
- Registry-Zugriffe (Credentials, Mirrors): damit ein Repull funktioniert.
- Volumes: je nach Umsetzung liegen sie unterhalb von DockerRootDir oder als separate Mounts. Prüfen Sie den tatsächlichen Speicherort.
Downtime-Plan (generisch, ohne Orchestrator)
# 1) Laufende Container geordnet stoppen
docker ps -q | xargs -r docker stop
# 2) Docker-Dienst stoppen
sudo systemctl stop docker
# 3) Datenverzeichnis sichern (Rollback-Pfad)
DOCKER_DIR=$(docker info --format '{{.DockerRootDir}}' 2>/dev/null || echo /var/lib/docker)
# Hinweis: Bei großen Verzeichnissen dauert das. Alternativ: Snapshot auf Storage-Ebene.
sudo tar -C "$(dirname "$DOCKER_DIR")" -cpf /root/docker-rootdir-backup.tar "$(basename "$DOCKER_DIR")"
# 4) Neues Filesystem/Blockdevice mounten und als DockerRootDir konfigurieren
# (Konkrete Mount-/mkfs-Schritte sind umgebungsspezifisch.)
# 5) Docker mit neuer Konfiguration starten
sudo systemctl start docker
# 6) Images neu ziehen und Workloads starten
# docker compose up -d (falls genutzt)Warum ein Tar-Backup als Rückfallpfad? Nicht, weil Sie es „immer“ zurückspielen wollen, sondern weil es Ihnen eine Option gibt, wenn unerwartete Abhängigkeiten im alten Root liegen (z. B. lokale Images ohne Registry, vergessene Daten in der Schreibschicht). In größeren Umgebungen ist ein Storage-Snapshot (LVM, SAN, VM-Snapshot mit Vorsicht) oft praktikabler.
DockerRootDir per daemon.json setzen
Viele Teams nutzen eine eigene Partition für Docker, um Platz, Inodes und IOPS klar zu kapseln. Das wird in /etc/docker/daemon.json konfiguriert.
{
"data-root": "/var/lib/docker",
"storage-driver": "overlay2"
}Wichtig: Ändern Sie storage-driver nicht „mal eben“ auf einem Host mit produktiven lokalen Layern, ohne den Neuaufbau einzuplanen. Und: Prüfen Sie nach einer Verlagerung SELinux/AppArmor-Kontexte und Mount-Optionen, weil sonst Folgefehler entstehen können (z. B. Permission Denied auf Label-basierten Systemen).
Best Practices für stabilen Betrieb (damit Sie nicht wieder im Incident landen)
1) Kapazität ist nicht nur GB: Inodes, Metadaten, IOPS
Planen Sie Monitoring auf mindestens drei Achsen: belegter Speicher, Inode-Auslastung und I/O-Latenz. Gerade Build-Hosts mit vielen Layern brauchen Inode-Reserven.
2) Volumes bewusst platzieren und trennen
Datenintensive Workloads (Datenbanken, Artefakt-Repos, Logs) sollten nicht auf der Container-Schreibschicht liegen. Nutzen Sie Volumes oder Bind-Mounts auf dafür ausgelegten Filesystemen. Das reduziert Copy-up und macht Performance planbarer.
3) Cleanup-Strategie definieren statt „manuell prune“
Regelmäßige Wartung ist besser als Ad-hoc-Löschen im Incident. Definieren Sie Regeln, wie lange Build-Caches, dangling images und alte Container auf dem Host bleiben dürfen. In CI-Umgebungen gehören dedizierte Runner-Hosts und ein kontrollierter Cache-Ansatz zur Betriebsroutine.
4) Kernel-/Filesystem-Änderungen wie Deployments behandeln
Ein Kernel-Update kann OverlayFS-Verhalten ändern; eine Filesystem-Migration kann d_type/Optionen beeinflussen. Testen Sie solche Änderungen auf einem Staging-Host mit repräsentativen Builds und I/O-Workloads, bevor Sie in die Breite gehen.
Schlussfazit: Storage-Probleme sind selten „mystisch“, aber oft mehrschichtig
OverlayFS, device-mapper und loopback wirken im Alltag wie interne Details – im Incident entscheiden sie aber über Stabilität, Performance und die Geschwindigkeit Ihrer Recovery. Ein sauberes Container-Storage-Troubleshooting beginnt mit einer klaren Baseline (Driver, Filesystem, DockerRootDir), trennt Layer-Probleme von Volume-Problemen und prüft konsequent Inodes, Thin-Pool-Metadaten und I/O-Latenz. Wenn loopback oder ein ungeeignetes Filesystem die Ursache ist, ist eine geplante Migration meist günstiger als dauerhaftes „Aufräumen“ unter Druck.
Wenn Sie das Thema in Ihrer Umgebung standardisieren wollen, legen Sie sich ein kurzes Runbook mit Prüfsequenz, Grenzwerten und einem getesteten Rückfallpfad an – dann wird aus dem nächsten Storage-Incident ein kontrollierbarer Wartungsfall.
Für dieses Thema sind auch Overlayfs Docker und Device-Mapper Docker wichtig. Der Beitrag ordnet diese Aspekte verständlich ein und zeigt, worauf es im Alltag ankommt.