Wat CrashLoopBackOff precies betekent
CrashLoopBackOff is een kubelet-displaystatus, geen officiële podfase. De daadwerkelijke podfase blijft Running of gaat naar Failed. Wat de status je vertelt: een container is gestart, gestopt, en de kubelet herstart hem met exponentieel oplopende wachttijden.
De backoff-reeks (Kubernetes 1.32 en eerder):
| Restart-poging | Wachttijd voor volgende poging |
|---|---|
| 1 | 10 seconden |
| 2 | 20 seconden |
| 3 | 40 seconden |
| 4 | 80 seconden |
| 5 | 160 seconden |
| 6+ | 300 seconden (maximum) |
Draait de container 10 minuten aaneengesloten zonder te crashen, dan reset de backoff naar 10 seconden.
Tijdens elke wachtperiode toont kubectl get pods de status CrashLoopBackOff. Zodra de kubelet een nieuwe poging doet, wisselt de status kort naar Running of ContainerCreating voordat de container opnieuw crasht.
Kubernetes 1.33 alpha-wijziging. De ReduceDefaultCrashLoopBackOffDecay feature gate (KEP-4603, opt-in, standaard uitgeschakeld) verlaagt de initiële wachttijd naar 1 seconde en kapt af op 60 seconden. Als je op 1.33 zit met deze gate ingeschakeld, herstellen je pods sneller van tijdelijke fouten, maar de diagnostische aanpak blijft gelijk.
De symptomen lezen
Drie commando's, in deze volgorde.
Stap 1: bevestig de crash loop
kubectl get pods -n <namespace>
Verwachte output voor een crashende pod:
NAME READY STATUS RESTARTS AGE
payment-svc-7b9f6d-xk2p 0/1 CrashLoopBackOff 14 (2m ago) 47m
Hoog getal bij RESTARTS, 0/1 onder READY en CrashLoopBackOff onder STATUS bevestigen de loop.
Stap 2: lees exit codes en events
kubectl describe pod payment-svc-7b9f6d-xk2p -n payments
Zoek naar twee secties in de output.
Containerstatus:
Last State: Terminated
Reason: Error
Exit Code: 1
Started: Wed, 09 Apr 2026 14:03:10 +0000
Finished: Wed, 09 Apr 2026 14:03:11 +0000
De exit code is je eerste vertakking. Zie de exit code referentietabel hieronder.
Events:
Warning BackOff 2m (x47 over 30m) kubelet Back-off restarting failed container
Zie je Liveness probe failed in de events? Dan crasht de container niet zelf. Kubernetes killt hem. Dat is een andere oorzaak.
Stap 3: lees de logs van de vorige container
kubectl logs payment-svc-7b9f6d-xk2p -n payments --previous
De --previous flag (of -p) haalt logs op van de laatst gestopte container-instantie. Zonder die flag krijg je de logs van de huidige container, die vaak leeg zijn omdat de container net is gestart.
Voor pods met meerdere containers:
kubectl logs payment-svc-7b9f6d-xk2p -n payments -c payment-worker --previous
Als --previous lege logs teruggeeft, crashte de container voordat er iets naar stdout/stderr geschreven werd. Dat wijst op een ontbrekend binary (exit code 127), een missende shared library, of een OOM kill in de eerste milliseconden. Ga naar debuggen als logs leeg zijn.
Exit code referentie
Exit codes verschijnen in kubectl describe pod onder Last State > Exit Code. De code vertelt je met welk type fout je te maken hebt.
| Exit code | Signaal | Wat het betekent | Meest waarschijnlijke Kubernetes-oorzaak |
|---|---|---|---|
| 0 | geen | Succesvolle exit | Container heeft taak afgerond en stopt; restartPolicy: Always herstart hem steeds |
| 1 | geen | Applicatiefout | Onafgevangen exceptie, missende config, fatale startfout |
| 126 | geen | Commando niet uitvoerbaar | Binary bestaat maar heeft verkeerde permissies |
| 127 | geen | Commando niet gevonden | Binary ontbreekt in image; verkeerd pad in command |
| 137 | SIGKILL | Geforceerde kill (128+9) | OOMKilled door de kernel, of directe SIGKILL |
| 139 | SIGSEGV | Segmentatiefout | Geheugentoegangsfout in applicatiecode |
| 143 | SIGTERM | Nette beeindiging (128+15) | Liveness probe-falen; kubelet stuurt SIGTERM |
De formule achter signaal-gebaseerde codes: 128 + signaalnummer. SIGKILL is signaal 9, dus 128 + 9 = 137.
Meest voorkomende oorzaken
Gerangschikt op hoe vaak ik ze tegenkom in productieclusters. De eerste vier dekken meer dan 90% van de gevallen.
Oorzaak 1: applicatiecrash (exit code 1)
De applicatie start, raakt een fatale fout en stopt. Logs tonen een stacktrace, panic of onafgevangen exceptie.
Typische logoutput:
- Python:
Exception: Database connection refused - Go:
panic: runtime error: nil pointer dereference - Node.js:
Error: Cannot find module './config' - Java:
java.lang.NullPointerException
Oplossing. Lees de stacktrace via kubectl logs --previous. Reproduceer lokaal met dezelfde image en omgevingsvariabelen:
docker run --rm \
-e DATABASE_URL=postgres://db-primary.internal:5432/payments \
-e LOG_LEVEL=debug \
registry.internal/payment-svc:3.1.4
Fix de bug. Bouw een nieuwe image met een unieke tag (niet latest). Roll hem uit:
kubectl set image deployment/payment-svc \
payment-svc=registry.internal/payment-svc:3.1.5 \
-n payments
kubectl rollout status deployment/payment-svc -n payments
Controleer: kubectl get pods -n payments toont 1/1 Running met 0 restarts.
Oorzaak 2: OOM kill (exit code 137)
De container overschreed zijn geheugenlimiet en de OOM killer van de Linux-kernel heeft hem beëindigd. Dit is een van de meest voorkomende oorzaken en heeft een duidelijke signatuur in kubectl describe pod:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Waarom het gebeurt: resources.limits.memory staat lager dan het daadwerkelijke geheugengebruik van de applicatie. Of de applicatie heeft een geheugenlek.
Snelle fix: verhoog de geheugenlimiet.
kubectl patch deployment payment-svc -n payments -p \
'{"spec":{"template":{"spec":{"containers":[{"name":"payment-svc","resources":{"limits":{"memory":"512Mi"},"requests":{"memory":"256Mi"}}}]}}}}'
Voordat je blindelings verhoogt, controleer of het geheugen oneindig blijft groeien (lek) of stabiliseert op een niveau boven de huidige limiet (te krap). kubectl top pod geeft een momentopname:
kubectl top pod payment-svc-7b9f6d-xk2p -n payments
Als je Prometheus met kube-state-metrics draait:
container_memory_working_set_bytes{container="payment-svc", namespace="payments"}
Een monotoon stijgende lijn betekent een lek. Fix de applicatie. Een lijn die stabiliseert boven de huidige limiet betekent dat de limiet te laag is. Verhoog hem.
Voor een dieper begrip van hoe requests en limits samenwerken, zie Kubernetes resource requests en limits.
Controleer: restart-teller stopt met oplopen, geen OOMKilled meer in kubectl describe pod.
Oorzaak 3: ontbrekende configuratie (ConfigMap, Secret, omgevingsvariabele)
De applicatie vereist een ConfigMap, Secret of omgevingsvariabele die niet bestaat in de namespace, een verkeerde naam heeft, of verwijderd is.
Hoe te bevestigen: logs tonen missing required env variable DATABASE_URL of config file not found: /etc/app/config.yaml. Soms start de pod helemaal niet en toont CreateContainerConfigError in plaats van CrashLoopBackOff.
# Controleer of de verwachte ConfigMap bestaat
kubectl get configmaps -n payments
# Controleer of het verwachte Secret bestaat
kubectl get secrets -n payments
# Bekijk wat de deployment verwacht
kubectl get deployment payment-svc -n payments -o yaml | grep -A 20 "env:"
Veelvoorkomende scenario's:
- ConfigMap verwijderd maar de Deployment verwijst er nog naar
- Secret bestaat in de
defaultnamespace maar de pod draait inpayments - Typfout in
valueFrom.secretKeyRef.key(de key binnen het Secret, niet de Secretnaam)
Oplossing. Maak de ontbrekende resource aan of corrigeer de referentie:
kubectl create secret generic payment-db-creds -n payments \
--from-literal=DB_PASSWORD=changeme-in-production
Controleer: pod start zonder config-gerelateerde fouten in kubectl logs.
Oorzaak 4: verkeerd geconfigureerde liveness probe
De container is gezond, maar Kubernetes blijft hem killen omdat de liveness probe faalt voordat de applicatie klaar is met opstarten. Dit is de meest misleidende oorzaak, want er is niks mis met de applicatie zelf.
Signatuur: kubectl describe pod toont Liveness probe failed in events. Exit code is 137 (SIGKILL van kubelet) of 143 (SIGTERM). De Last State van de container laat hele korte draaitijden zien (1–5 seconden).
Het meest voorkomende patroon: initialDelaySeconds is korter dan de opstarttijd van de applicatie. Een Java-service die 45 seconden nodig heeft om de Spring-context te laden, de database te verbinden en caches op te warmen, faalt een probe die begint bij initialDelaySeconds: 5.
Andere probe-misconfiguraties:
- Verkeerd
path: probe raakt/healthzmaar de applicatie serveert/health - Verkeerde
port: probe checkt 8080 maar de applicatie bindt op 8081 timeoutSeconds: 1terwijl het endpoint 2 seconden nodig heeft onder loadfailureThreshold: 1: een enkele timeout triggert een restart
Snelle test om te bevestigen. Verwijder de liveness probe tijdelijk:
kubectl patch deployment payment-svc -n payments --type json \
-p '[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'
Stoppen de restarts? Dan was de probe het probleem.
Goede fix: voeg een startup probe toe. In plaats van te vechten met initialDelaySeconds, gebruik een startup probe die de liveness probe blokkeert totdat de applicatie klaar is:
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 30 x 10s = 300s maximale opstarttijd
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
Voor een volledig overzicht van probetypes, timingparameters en endpointdesign, zie Kubernetes health probes configureren.
Controleer: pod blijft Running tijdens het opstarten. Geen Liveness probe failed-events in kubectl describe pod.
Oorzaak 5: verkeerd entrypoint of ontbrekend binary (exit code 127/128)
De podspec definieert een command of args die niet bestaat in de image. De container stopt meteen met exit code 127 (niet gevonden) of 126 (niet uitvoerbaar). Logs zijn meestal leeg.
Belangrijk onderscheid: in Kubernetes overschrijft command Docker's ENTRYPOINT en args overschrijft Docker's CMD. Als je command zet in de podspec, wordt het ENTRYPOINT van de image volledig genegeerd.
Oplossing. Inspecteer de image om het juiste pad te vinden:
docker inspect registry.internal/payment-svc:3.1.4 \
| jq '.[0].Config.Entrypoint, .[0].Config.Cmd'
Of draai een tijdelijke pod om het bestandssysteem te verkennen:
kubectl run debug-payment --rm -it \
--image=registry.internal/payment-svc:3.1.4 \
--restart=Never -- /bin/sh
# In de container:
find / -name payment-svc 2>/dev/null
Corrigeer command of args in de Deploymentspec.
Controleer: container start en produceert logoutput.
Oorzaak 6: container eindigt succesvol (exit code 0)
De container stopt met code 0 (succes), maar restartPolicy: Always (de default voor Deployments) herstart hem telkens opnieuw. Dit gebeurt als een batch-job-image als Deployment uitgerold wordt in plaats van als Job.
Opties:
- Schakel de workload om naar een
JobofCronJobals het een batchtaak is - Fix het containercommando zodat het een persistent proces draait (
exec payment-svc servein plaats vanpayment-svc run-once) - Zet
restartPolicy: Neverals de workload echt niet hoeft te herstarten (alleen geldig in losse pods, niet in Deployments)
Debuggen als logs leeg zijn
Als kubectl logs --previous niets teruggeeft, crashte de container voordat er iets naar stdout geschreven werd. Twee technieken om toch binnen te komen.
Ephemeral containers (Kubernetes 1.23+)
Ephemeral containers koppelen een debugcontainer aan een draaiende (of crashende) pod. Ze delen de procesnamespace met de doelcontainer.
kubectl debug -it payment-svc-7b9f6d-xk2p -n payments \
--image=busybox:1.36 \
--target=payment-svc
Eenmaal binnen kun je het bestandssysteem en de processen van de doelcontainer inspecteren:
# Controleer of het binary bestaat
ls -la /proc/1/root/usr/local/bin/
# Controleer shared library-afhankelijkheden
ldd /proc/1/root/usr/local/bin/payment-svc
# Bekijk omgevingsvariabelen
cat /proc/1/environ | tr '\0' '\n'
Copy-and-override debugging
Maak een kopie van de crashende pod met een ander commando dat hem in leven houdt:
kubectl debug payment-svc-7b9f6d-xk2p -it \
--copy-to=payment-debug \
-n payments \
-- sleep infinity
Exec er dan in en draai het originele commando handmatig om de fout te zien:
kubectl exec -it payment-debug -n payments -- /bin/sh
# Draai het originele entrypoint
/usr/local/bin/payment-svc serve --config /etc/payment/config.yaml
Ruim de debug-pod op als je klaar bent: kubectl delete pod payment-debug -n payments.
Monitoring en alerting
Als je kube-state-metrics met Prometheus draait, kun je op CrashLoopBackOff alerteren voordat iemand het handmatig opmerkt.
CrashLoopBackOff direct detecteren:
kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1
Alertregel op hoge restart-rate (vangt loops eerder op):
# Prometheus alerting rule
- alert: PodCrashLooping
expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
for: 10m
labels:
severity: warning
annotations:
summary: "Pod / herstartte >5 keer in 1u"
De restart-rate query viert voordat de backoff het 5-minutenmaximum bereikt, dus je krijgt eerder een waarschuwing.
Herhaling voorkomen
- Pin image-tags.
payment-svc:3.1.4, nietpayment-svc:latest. Gepinde tags maken rollbacks deterministisch metkubectl rollout undo. - Zet altijd memory requests en limits. Geen limiet betekent geen geheugengrens; de kernel killt je pod zonder waarschuwing als de node krap zit.
- Test containers lokaal eerst.
docker runmet dezelfde omgevingsvariabelen vangt de meeste opstartfouten voordat ze het cluster bereiken. - Gebruik startup probes voor trage applicaties. Rek
initialDelaySecondsniet op naar 120 seconden. Daar zijn startup probes voor. - Verifieer dat dependencies bestaan voor het deployen.
kubectl get configmaps,secrets -n paymentsvoordat jekubectl apply -f deployment.yamldraait. - Zet JVM-heap en GOMAXPROCS expliciet. Java- en Go-processen gebruiken standaard al het beschikbare geheugen en alle cores van de node, niet die van de container. De JVM respecteert
-XX:MaxRAMPercentagesinds Java 10. Go respecteert deGOMEMLIMITomgevingsvariabele sinds Go 1.19.
Wanneer escaleren
Als geen van de bovenstaande oorzaken past, of als fixes de loop niet oplossen, verzamel dan deze informatie voordat je hulp vraagt:
- Volledige output van
kubectl describe pod <pod-name> -n <namespace> - Logs van de vorige container:
kubectl logs <pod-name> -n <namespace> --previous - Namespace-events:
kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp - Node-resourcedruk:
kubectl top nodeenkubectl describe node <node-name> - Kubernetes-versie:
kubectl version - Het Deployment-manifest (zonder secrets)
- Of het probleem consistent of intermitterend is
- Of dezelfde image werkt in een andere namespace of cluster