Kubernetes · intermediate
Kubernetes CrashLoopBackOff checklist
A structured triage checklist for Kubernetes CrashLoopBackOff, distinguishing container startup crashes from passing-but-unhealthy probes. Walks through ordered evidence gathering, log boundaries, manifest review, and safe mitigation actions tied to observable proof criteria.
The symptoms
- •Pod STATUS stuck in CrashLoopBackOff with RESTARTS counter incrementing while the container repeatedly exits non-zero within seconds of start.
- •kubectl describe pod shows Last State: Terminated with a non-zero Exit Code and a short Finished timestamp indicating fast failure rather than a long-running crash.
- •Events column displays Back-off restarting failed container with a steadily increasing exponential delay (e.g., 5s, 10s, 20s, 40s) between restart attempts.
- •kubectl get pod output shows READY 0/1 with the container reporting Started: false or Ready: false even after waiting past the restart delay window.
- •Application logs retrieved via kubectl logs --previous end abruptly with stack traces, panic messages, or immediate process exit rather than steady request-serving activity.
Likely causes
- •Missing or invalid configuration values, such as required environment variables, ConfigMap keys, or Secret references, causing the process to exit before binding to its listening port.
- •Image or entrypoint mismatch, where the image tag is wrong, the ENTRYPOINT/CMD is malformed, or the working directory contains a missing script or binary path.
- •Readiness or liveness probe misconfiguration where the probe path, port, scheme, or initialDelaySeconds does not match the application's actual startup behavior, so a running container is repeatedly killed.
- •Resource starvation at startup, where the container's requests for CPU or memory are below what the application needs to initialize, causing an OOMKill on the very first attempts.
- •Downstream dependency failure, such as an unreachable database, DNS resolution failure to an external service, or a service endpoint that has no ready endpoints during early startup.
First ten minutes
- 01Confirm scope by running kubectl get pods -n <namespace> -l <selector> and recording which pod(s) show CrashLoopBackOff versus ImagePullBackOff or ErrImagePull; CrashLoopBackOff specifically means the image started but exited.
- 02Run kubectl describe pod <pod> -n <namespace> and read the Events column from oldest to newest to capture the Back-off restarting failed container messages with their timestamps and the Last State exit reason.
- 03Fetch the current container output with kubectl logs <pod> -n <namespace> --previous --tail=200 to see the last failed attempt, then compare against kubectl logs <pod> -n <namespace> --tail=200 to confirm whether the container has ever logged steady-state activity.
- 04Decide whether the exit reason in describe (Error, OOMKilled, Completed) points to a startup crash, a resource kill, or a process that simply ran and exited; this determines whether to investigate manifest, resources, or workload design.
- 05Verify the workload definition with kubectl get pod <pod> -n <namespace> -o yaml and inspect containers[].image, envFrom, volumeMounts, and probes for typos, missing keys, or absolute paths that do not exist inside the image filesystem.
- 06Temporarily disable suspect probes in a copy of the manifest (do not edit the live workload) to test whether the container reaches Ready without probe interference; revert immediately after observation.
Evidence to collect
- •Exact exit code and exit reason from kubectl describe pod, recorded under Last State > Terminated > Reason and Exit Code, plus the Finished timestamp delta to determine fast-fail versus slow-fail pattern.
- •Full stdout/stderr from the failed attempt via kubectl logs --previous, including any panic lines, stack traces, or messages printed immediately before process exit.
- •Probe configuration from the pod spec: httpGet path and port, tcpSocket port, exec command, initialDelaySeconds, periodSeconds, failureThreshold, and timeoutSeconds, cross-referenced with the application's actual readiness behavior.
- •Resource settings: containers[].resources.requests and limits for CPU and memory, plus the node's allocatable capacity and current usage via kubectl describe node, to evaluate whether OOMKill is plausible.
- •Dependency reachability evidence: Service and Endpoints state for any referenced hostname, DNS resolution inside the pod via a short-lived debug container, and Secret/ConfigMap presence via kubectl get with --ignore-not-found.
- •Event timeline of Back-off restarting failed container messages with their respective restart counts to confirm the loop is exponential and not triggered by node pressure or evictions.
Where to look
- •At the pod boundary: kubectl describe pod <pod> -n <namespace> output, specifically the Conditions, Containers, and Events sections in that order.
- •At the container boundary: kubectl logs <pod> -n <namespace> --previous for the most recent terminated instance output and the live logs for the current attempt.
- •At the workload boundary: the pod spec rendered by kubectl get pod <pod> -n <namespace> -o yaml, particularly the containers, initContainers, and volumes blocks including projected ConfigMap and Secret sources.
- •At the cluster scheduling boundary: kubectl describe node events and the kubelet-reported Conditions, plus any FailedScheduling or NodeAffinity-related messages that could prevent the pod from ever reaching a node.
- •At the dependency boundary: kubectl get endpoints -n <namespace> for backing Services to confirm there are real endpoint addresses, and resolve any external hostnames from inside a debug pod to validate DNS and network reachability.
Diagnostic steps
- 01Differentiate fast-crash from probe-kill by reading Last State Exit Code: an Error exit within the first few seconds of container start indicates an application startup failure, whereas a later termination with reason Completed or a probe failure message points to probe misconfiguration.
- 02Compare the container's startup logs against the probe's initialDelaySeconds; if logs show the process binding to its port and serving requests only after the probe has already failed repeatedly, the probe is too aggressive.
- 03Reproduce locally by running the same image and command outside the cluster with the same environment variables to confirm the failure is environmental rather than cluster-specific.
- 04Inspect initContainers status in describe; a failed initContainer will surface as a non-zero exit reason before the main container ever starts, so check each initContainer's Last State before assuming the application container is at fault.
- 05Validate mounted configuration by exec'ing into the pod with kubectl debug or by mounting a debug sidecar to read /etc/config or the configured mount path and confirm expected keys are present and correctly formatted.
- 06Correlate restart timestamps with node-level pressure events; if restarts coincide with MemoryPressure or DiskPressure transitions on the node, root cause is node capacity rather than application code.
- 07Use kubectl get events --sort-by=.lastTimestamp in the pod's namespace to map the Back-off restarting failed container events against any ImagePull, FailedMount, or FailedCreatePodSandBox events that may be co-occurring.
Common mistakes
- •Restarting the pod as a first action without reading the previous-instance logs, which destroys the only record of the actual failure reason and obscures whether the exit was a crash or a probe kill.
- •Increasing livenessProbe failureThreshold or initialDelaySeconds blindly, which can mask a real startup bug and only delay the inevitable restart loop while breaking restart-backoff math.
- •Editing the live Deployment while probes are still misconfigured, which causes rolling updates that compound the loop across multiple replicas and makes the events timeline harder to read.
- •Assuming CrashLoopBackOff always means an application bug; the loop can also be triggered by missing ConfigMaps/Secrets, initContainer failures, or node-level resource exhaustion.
- •Confusing Ready: false due to a failing readiness probe with CrashLoopBackOff; readiness failures typically do not increment RESTARTS and the container stays Running, not in the CrashLoopBackOff status.
- •Logging into the container with a shell to inspect state when the container exits so quickly that any session terminates immediately; use kubectl debug or read logs and manifests instead.
Safe fixes
- •If the previous logs show a missing file or environment variable, patch the workload via a copy of the manifest to add the required Secret/ConfigMap and env entries, then apply it to a non-production replica set first; verify the new pod reaches Ready: true and RESTARTS stays at zero for at least one probe period.
- •If the exit reason is OOMKilled, raise the memory limit in the workload copy only after confirming via metrics that the application legitimately needs more memory at startup; verify by observing the new pod remains Running across at least three probe cycles with stable memory usage below the new limit.
- •If a readiness or liveness probe is mismatched, adjust the probe definition in a manifest copy to point at the correct path/port and set initialDelaySeconds to at least the observed application startup time; verify by checking that the container reaches Ready without RESTARTS incrementing over a window equal to several periodSeconds.
- •If an initContainer is failing, fix the initContainer command or image in a manifest copy before the main container can ever succeed; verify by confirming all initContainers report Ready and the main container's Last State transitions to Running.
- •If downstream endpoints are missing, fix the backing Service selector or scale the dependent workload; verify via kubectl get endpoints that endpoint addresses exist and a debug resolution returns the expected cluster IP.
Prove the fix
- 01Confirm kubectl get pod <pod> -n <namespace> shows STATUS Running, READY 1/1, and RESTARTS unchanged from the moment the fix was applied for a window of at least 5x the probe periodSeconds.
- 02Confirm kubectl describe pod <pod> -n <namespace> shows no new Back-off restarting failed container events since the fix timestamp and the Last State section is empty or older than the fix.
- 03Confirm kubectl logs <pod> -n <namespace> --tail=200 shows steady-state application activity (request handling, health endpoints responding) rather than repeated startup banners or panic traces.
- 04Confirm the probe behavior matches expectations: kubectl describe pod shows the liveness/readiness probes in the Containers section with results that have transitioned to Success at least once after the fix.
- 05Confirm that scaling the same fixed manifest to additional replicas also yields Running and Ready pods with zero restarts, proving the fix is not replica-specific.
Prevention and next steps
- •Adopt a startup probe (startupProbe) for slow-starting applications so liveness checks do not compete with initialization, and align initialDelaySeconds with measured cold-start time.
- •Pin image digests rather than mutable tags in production manifests so a rebuilt image with a broken entrypoint cannot be pulled silently and trigger an unexpected crash loop.
- •Enforce pre-deployment validation of required ConfigMap and Secret keys via admission policies or CI schema checks so missing configuration surfaces before the pod ever starts.
- •Set resource requests and limits based on measured startup and steady-state usage, and monitor OOMKilled events across namespaces to detect starvation before it manifests as a loop.
- •Keep initContainers minimal and idempotent, and treat any change to init container commands as a release-blocking change requiring the same review as main container changes.
Safe commands and checks
kubectl get pods -n <namespace> -l <selector> -o wide kubectl describe pod <pod> -n <namespace> kubectl logs <pod> -n <namespace> --previous --tail=200 kubectl logs <pod> -n <namespace> --tail=200 kubectl get pod <pod> -n <namespace> -o yaml kubectl get events -n <namespace> --sort-by=.lastTimestamp kubectl get endpoints -n <namespace> <service> kubectl describe node <node> | grep -A20 Conditions