Docker-Container startet nicht? Systematische Fehlersuche
Systematischer Ansatz zum Debuggen von Docker-Containern. Exit-Codes, Logs, Compose-Probleme, Berechtigungsprobleme.
Kurzfassung
Wenn ein Docker-Container nicht startet, arbeite systematisch vor: Pruefe zuerst docker logs auf Anwendungsfehler, nutze docker inspect fuer den Container-Zustand und den Exit-Code, ueberpruefe Port-Verfuegbarkeit und Volume-Berechtigungen, und kontrolliere den freien Speicherplatz. Die haeufigsten Ursachen fuer Startprobleme lassen sich auf fuenf Kategorien eingrenzen: Anwendungsfehler (Exit-Code 1), fehlende Binaries (127), Speicherlimit ueberschritten (137), Berechtigungsprobleme oder Port-Konflikte. Diese Anleitung fuehrt mit konkreten Befehlen durch jedes Szenario.
Voraussetzungen
- Docker Engine 20.10+ installiert und laufend (Pruefung mit
docker version) - Docker Compose v2 (in modernen Docker-Desktop-Versionen enthalten oder separat installiert)
- Terminal-Zugang mit Berechtigung fuer Docker-Befehle (Benutzer muss in der Gruppe
dockersein odersudoverwenden) - Grundkenntnisse in der Kommandozeile und im Umgang mit Containern
Schritt-fuer-Schritt Debugging-Anleitung
1. Container-Logs auslesen
Hier faengt jede Fehlersuche an. Container-Logs erfassen alles, was der Hauptprozess der Anwendung auf stdout und stderr schreibt.
# Alle Logs eines Containers anzeigen (auch gestoppte Container)
docker logs mein-container
# Nur die letzten 50 Zeilen anzeigen
docker logs --tail 50 mein-container
# Logs in Echtzeit verfolgen (wie tail -f)
docker logs --tail 50 -f mein-container
# Zeitstempel zu jeder Zeile anzeigen
docker logs -t mein-container
# Logs ab einem bestimmten Zeitpunkt
docker logs --since 2025-01-01T10:00:00 mein-container
Wenn der Container sofort beendet wurde, enthalten die Logs meistens die genaue Fehlermeldung. Sind die Logs leer, liegt das Problem wahrscheinlich auf Container-Runtime-Ebene: falscher Entrypoint, fehlendes Binary oder keine Ausfuehrungsberechtigung.
2. Container-Zustand untersuchen
docker inspect liefert saemtliche Informationen, die Docker ueber einen Container hat: Konfiguration, Zustand, Netzwerkeinstellungen und mehr.
# Vollstaendige JSON-Ausgabe
docker inspect mein-container
# Nur den Zustand abfragen (Status, Exit-Code, Fehler)
docker inspect --format '{{json .State}}' mein-container | jq .
# Exit-Code direkt auslesen
docker inspect --format '{{.State.ExitCode}}' mein-container
# OOM-Killer-Flag pruefen
docker inspect --format '{{.State.OOMKilled}}' mein-container
# Den ausgefuehrten Befehl anzeigen
docker inspect --format '{{.Config.Cmd}}' mein-container
# Entrypoint pruefen
docker inspect --format '{{.Config.Entrypoint}}' mein-container
# Volume-Konfiguration anzeigen
docker inspect --format '{{json .Mounts}}' mein-container | jq .
3. Exit-Codes verstehen
Der Exit-Code verraet, wie der Prozess beendet wurde. Die folgende Tabelle deckt die wichtigsten Faelle ab:
| Exit-Code | Bedeutung | Typische Ursache |
|---|---|---|
| 0 | Erfolg | Der Prozess wurde normal beendet. Wenn der Container "nicht laufen bleibt," ist vermutlich der Entrypoint ein Skript, das durchlaeuft statt zu blockieren. |
| 1 | Anwendungsfehler | Allgemeiner Fehler. docker logs enthaelt die eigentliche Fehlermeldung. |
| 126 | Keine Ausfuehrungsberechtigung | Die Entrypoint-Datei existiert, ist aber nicht ausfuehrbar. |
| 127 | Befehl nicht gefunden | Das Binary in CMD/ENTRYPOINT existiert nicht im Container. Haeufig bei Multi-Stage-Builds, wenn das COPY vergessen wurde. |
| 137 | SIGKILL (OOM) | Der Container hat sein Speicherlimit ueberschritten und wurde vom OOM-Killer beendet. Bestaetigung mit docker inspect --format '{{.State.OOMKilled}}'. |
| 139 | SIGSEGV | Segmentation Fault in der Anwendung. Tritt haeufig auf, wenn ein amd64-Binary auf arm64 ausgefuehrt wird (oder umgekehrt). |
| 143 | SIGTERM | Der Prozess hat ein Beendigungssignal erhalten. Normal bei docker stop, aber unerwartet beim Start deutet auf einen Orchestrator hin, der den Container stoppt. |
4. Interaktives Debugging mit exec
Wenn der Container laeuft, aber sich falsch verhaelt, kannst du direkt hineinspringen:
# Shell in einem laufenden Container oeffnen
docker exec -it mein-container /bin/sh
# Falls sh nicht verfuegbar ist, bash versuchen
docker exec -it mein-container /bin/bash
# Einzelnen Diagnosebefehl ausfuehren
docker exec mein-container cat /etc/os-release
docker exec mein-container ls -la /app/
Wenn der Container sofort beendet wird und kein exec moeglich ist, ueberschreibe den Entrypoint:
# Image mit Shell statt normalem Entrypoint starten
docker run -it --entrypoint /bin/sh mein-image
# Oder mit sleep am Leben halten, um dann exec zu nutzen
docker run -d --entrypoint sleep mein-image 3600
docker exec -it $(docker ps -q -f ancestor=mein-image) /bin/sh
5. Docker-Compose-Probleme
Port-Konflikte
Wenn ein Service nicht startet, weil der Port bereits belegt ist:
# Herausfinden, welcher Prozess einen Port belegt
lsof -i :8080
# Unter Linux alternativ:
ss -tlnp | grep 8080
# Prozess bei Bedarf beenden
kill -9 $(lsof -t -i :8080)
Netzwerkprobleme
# Alle Docker-Netzwerke auflisten
docker network ls
# Bestimmtes Netzwerk untersuchen (zeigt verbundene Container)
docker network inspect meine-app_default
# Konnektivitaet zwischen Containern testen
docker exec mein-container ping anderer-container
# DNS-Aufloesung innerhalb eines Containers pruefen
docker exec mein-container nslookup anderer-container
depends_on vs. Healthchecks
depends_on wartet nur darauf, dass der Container gestartet wird — nicht darauf, dass die Anwendung darin bereit ist. Ein Datenbank-Container braucht vielleicht 10 Sekunden zur Initialisierung, aber die App versucht sofort eine Verbindung herzustellen.
# docker-compose.yml mit korrektem Healthcheck
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
app:
image: meine-app
depends_on:
db:
condition: service_healthy
Volume-Berechtigungen
# Besitzrechte gemounteter Volumes pruefen
docker exec mein-container ls -la /data/
# Pruefen, als welcher Benutzer der Container-Prozess laeuft
docker exec mein-container id
# Besitzrechte auf dem Host korrigieren
sudo chown -R 1000:1000 ./data/
6. Berechtigungsprobleme
Berechtigungsfehler gehoeren zu den frustrierendsten Docker-Problemen, besonders im Zusammenspiel mit gemounteten Volumes.
# Schlecht: Dateien als root kopiert, dann auf non-root User gewechselt
FROM node:20-alpine
COPY . /app
USER node
CMD ["node", "/app/server.js"]
# Richtig: Besitzrechte explizit setzen
FROM node:20-alpine
COPY --chown=node:node . /app
WORKDIR /app
USER node
CMD ["node", "server.js"]
Bei Bind-Mounts von Host-Verzeichnissen muss die UID im Container mit dem Dateibesitzer auf dem Host uebereinstimmen:
# UID des Container-Benutzers pruefen
docker exec mein-container id
# uid=1000(node) gid=1000(node)
# Sicherstellen, dass Host-Dateien uebereinstimmen
ls -ln ./data/
# Bei Abweichung korrigieren:
sudo chown -R 1000:1000 ./data/
7. Multi-Stage-Build-Fehler
Multi-Stage-Builds sind eine haeufige Quelle fuer "Container startet, aber Binary/Datei fehlt"-Probleme.
# Haeufiger Fehler: COPY aus der Build-Stage vergessen
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o /app/server .
FROM alpine:3.19
# FEHLER: Binary wurde nicht kopiert!
CMD ["/app/server"]
# Exit-Code 127: /app/server nicht gefunden
# Korrektur:
FROM alpine:3.19
COPY --from=builder /app/server /app/server
CMD ["/app/server"]
Ein weiteres haeufiges Problem: auf einem glibc-basierten Image (Debian/Ubuntu) bauen und auf einem musl-basierten Image (Alpine) ausfuehren. Das Binary erzeugt einen Segfault (Exit-Code 139) oder findet keine Shared Libraries. Entweder die gleiche Basis verwenden oder statisch kompilieren:
# Fuer Go: vollstaendig statisches Binary bauen
CGO_ENABLED=0 go build -o /app/server .
8. Speicherplatzprobleme
Docker kann unbemerkt enorme Mengen an Speicherplatz verbrauchen. Ist die Festplatte voll, scheitern Container-Starts mit kryptischen Fehlermeldungen.
# Docker-Speicherverbrauch mit Aufschluesselung anzeigen
docker system df
# Detaillierte Ansicht
docker system df -v
# Ungenutzte Daten entfernen (gestoppte Container, verwaiste Images, ungenutzte Netzwerke)
docker system prune
# Auch ungenutzte Volumes entfernen (VORSICHT: loescht Daten)
docker system prune --volumes
# Alle ungenutzten Images entfernen, nicht nur verwaiste
docker system prune -a
# Festplattenbelegung auf dem Host pruefen
df -h /var/lib/docker
9. Umgebungsvariablen pruefen
Falsch konfigurierte Umgebungsvariablen sind ein stiller Killer. Die App startet, verbindet sich aber mit der falschen Datenbank, nutzt den falschen API-Key oder faellt auf Standardwerte zurueck.
# Alle Umgebungsvariablen in einem laufenden Container auflisten
docker exec mein-container env
# Bestimmte Variable pruefen
docker exec mein-container printenv DATABASE_URL
# Umgebungsvariablen per docker inspect (funktioniert auch bei gestoppten Containern)
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' mein-container
# Pruefen, ob die .env-Datei in Compose korrekt geladen wird
docker compose config
docker compose config ist besonders nuetzlich: Es rendert die finale, aufgeloeste YAML-Datei mit allen Variablen-Substitutionen, sodass sich ueberpruefen laesst, ob ${VARIABLEN} korrekt aufgeloest wurden.
Schnellreferenz zur Fehlerbehebung
| Symptom | Erster Befehl | Wahrscheinliche Ursache |
|---|---|---|
| Container beendet sich sofort | docker logs mein-container | Anwendungsabsturz oder Entrypoint laeuft durch |
| Exit-Code 137, keine Fehler in Logs | docker inspect --format '{{.State.OOMKilled}}' | Speicherlimit ueberschritten |
| "port already in use" | lsof -i :PORT | Anderer Prozess oder Container belegt den Port |
| "permission denied" | docker exec mein-container ls -la /pfad | UID-Abweichung zwischen Container-Benutzer und Dateibesitzer |
| "no such file or directory" | docker exec mein-container ls /app/ | Fehlendes COPY im Dockerfile oder falscher Pfad |
| Container laeuft, aber App nicht erreichbar | docker network inspect | Netzwerk-Fehlkonfiguration oder falscher publizierter Port |
| "no space left on device" | docker system df | Docker-Images/Volumes belegen gesamten Speicherplatz |
| Compose-Services finden sich nicht | docker compose config | Services in verschiedenen Netzwerken oder Tippfehler im Service-Namen |
Praevention
- Immer Healthchecks definieren — sowohl in Dockerfiles als auch in Compose-Services. Sie erkennen Startfehler frueh und integrieren sich mit Orchestrierern.
depends_onmitcondition: service_healthyverwenden statt blankemdepends_on. Die Startreihenfolge von Containern ist kein zuverlaessiger Indikator fuer Anwendungsbereitschaft.- Base-Images auf konkrete Tags pinnen (z.B.
node:20.11-alpine3.19), nichtlatest. Ein Base-Image-Update kann den Build unbemerkt brechen. - Speicherlimits explizit setzen mit
--memoryoder demmem_limit-Key in Compose. Ohne Limits kann ein Memory Leak den gesamten Host lahmlegen. docker system pruneregelmaessig ausfuehren oder Dockersstorage-optszur Groessenbegrenzung konfigurieren. CI-Server sind dafuer beruechtigt, Festplatten mit Image-Layern zu fuellen..dockerignoreverwenden, um den Build-Kontext klein zu halten. Grosse Kontexte verlangsamen Builds und koennen versehentlich Secrets wie.env-Dateien oder das.git-Verzeichnis einschliessen.- Compose-Datei vor dem Deployment validieren:
docker compose config --quietgibt einen Fehlercode zurueck, wenn die Syntax fehlerhaft ist oder Variablen nicht aufgeloest werden konnten. - Auf stdout/stderr loggen, nicht in Dateien innerhalb des Containers. Nur so funktioniert
docker logszuverlaessig und die Integration mit Log-Aggregation-Tools ist gewaehrleistet. - Lokal mit demselben Image testen, das auch deployed wird. Unterschiedliche Dockerfiles fuer Entwicklung und Produktion fuehren frueher oder spaeter zu Problemen, die nur in einer Umgebung auftreten.
Experten-Hilfe gebraucht?
Bug nicht gefunden? Ich debugge live per Screensharing. €49, 30 Min.
Jetzt buchen — €49100% Geld-zurück-Garantie