SRE toil is the manual, repetitive, tactical work that comes from running a production service.

Here’s a Kubernetes cluster experiencing intermittent Pod restarts due to CrashLoopBackOff errors. This isn’t just a simple application crash; it’s the Kubernetes control plane giving up on a Pod because it can’t maintain a healthy state. The interesting part is that the kubelet on the node is reporting the Pod is unhealthy, but the root cause is often an upstream resource constraint or misconfiguration that prevents the Pod from even starting correctly.

Here are the common culprits:

  • Resource Limits Exceeded: The Pod is requesting more CPU or memory than the node can provide, or its configured limits are too low and it’s being OOMKilled.

    • Diagnosis: kubectl describe pod <pod-name> -n <namespace>. Look for OOMKilled in the Last State or Reason fields. Also, kubectl top pod <pod-name> -n <namespace> can show current resource usage. Check kubectl describe node <node-name> for overall node capacity and allocatable resources.
    • Fix: Increase resource requests and limits in the Pod’s YAML. For example, change resources: { requests: { cpu: "200m", memory: "256Mi" }, limits: { cpu: "500m", memory: "512Mi" } } to resources: { requests: { cpu: "400m", memory: "512Mi" }, limits: { cpu: "1000m", memory: "1024Mi" } }.
    • Why it works: This gives the container more guaranteed resources and a higher ceiling, preventing it from being terminated due to memory pressure or CPU starvation.
  • ImagePullBackOff / ErrImagePull: The kubelet cannot pull the container image.

    • Diagnosis: kubectl describe pod <pod-name> -n <namespace>. Look for ImagePullBackOff or ErrImagePull in the Events section. Check kubectl get pods -n <namespace> -o wide to see which node the pod is scheduled on and then ssh <node-name> and run sudo journalctl -u kubelet -f to see more detailed logs from the kubelet trying to pull the image.
    • Fix: Ensure the image name and tag are correct in the Pod spec. If using a private registry, verify that the imagePullSecrets are correctly configured in the Pod or ServiceAccount and that the secret itself is valid. For example, if the image is myregistry.com/myimage:v1.2.3, ensure it’s spelled exactly like that. If using a private registry, ensure the secret my-registry-secret exists in the same namespace: kubectl get secret my-registry-secret -n <namespace>.
    • Why it works: This resolves the issue by pointing the kubelet to the correct image location or providing valid credentials for private repositories, allowing it to download the necessary container image.
  • Readiness Probe Failure: The application inside the Pod is not responding to its readiness probe within the configured timeout.

    • Diagnosis: kubectl describe pod <pod-name> -n <namespace>. Check the Events for repeated probe failures (e.g., Readiness probe failed: HTTP request failed). Also, kubectl logs <pod-name> -n <namespace> will show application logs that might indicate why it’s not ready.
    • Fix: Adjust the probe’s initialDelaySeconds, periodSeconds, timeoutSeconds, or failureThreshold. For example, increase initialDelaySeconds from 5 to 15 if the application takes longer to start: livenessProbe: { httpGet: { path: /healthz, port: 8080 }, initialDelaySeconds: 15, periodSeconds: 10 }.
    • Why it works: This allows the application more time to initialize or increases the tolerance for temporary unresponsiveness before Kubernetes considers the Pod unhealthy and restarts it.
  • Persistent Volume Claim (PVC) Not Bound: The Pod is waiting for a PersistentVolumeClaim to be bound to a PersistentVolume, but this binding is failing.

    • Diagnosis: kubectl describe pvc <pvc-name> -n <namespace>. Look for events indicating why it’s not binding (e.g., no matching PVs, storage class issues). Also, kubectl get pv and kubectl get sc to check available volumes and storage class configurations.
    • Fix: Ensure a PersistentVolume exists that matches the storageClassName and accessModes requested by the PVC, or that the StorageClass is correctly configured to dynamically provision volumes. For example, if the PVC requests storageClassName: gp2 and accessModes: [ReadWriteOnce], ensure a gp2 storage class is available and can provision a ReadWriteOnce volume.
    • Why it works: This ensures that the underlying storage is available and correctly provisioned or bound, allowing the Pod to mount its required volumes and start successfully.
  • Network Policy Blocking Traffic: A NetworkPolicy is preventing the Pod from receiving necessary inbound or outbound traffic, leading to timeouts or connection failures that trigger health checks.

    • Diagnosis: kubectl get networkpolicy -n <namespace>. Review policies to see if they are overly restrictive. Use tcpdump on the node where the pod is running to inspect traffic to/from the pod’s IP address.
    • Fix: Modify the NetworkPolicy to allow the required traffic. For instance, if a Pod needs to receive traffic on port 8080 from other pods in the same namespace, a policy might look like: podSelector: {} and ingress: [ { from: [ {} ], ports: [ { protocol: TCP, port: 8080 } ] } ].
    • Why it works: This relaxes network restrictions, permitting the Pod to communicate with other services or receive external requests essential for its operation and health checks.
  • Init Container Failures: The Pod has initContainers that are failing to complete their tasks before the main application containers start.

    • Diagnosis: kubectl logs <pod-name> -c <init-container-name> -n <namespace>. The initContainers run to completion before application containers. If they fail, the Pod will restart with CrashLoopBackOff.
    • Fix: Debug the initContainer’s logic by examining its logs. Common issues include incorrect configuration, missing dependencies, or failure to perform its setup task (e.g., initializing a database schema, downloading configuration files). For example, if an initContainer fails because it can’t download a config file, fix the initContainer’s command or mount a ConfigMap correctly.
    • Why it works: This ensures that prerequisite tasks are successfully completed by the initContainers, allowing the main application containers to start in a properly configured environment.

The next error you’ll likely encounter after resolving CrashLoopBackOff is ImagePullBackOff if your image registry is temporarily unavailable, or a probe-related error if your application logic is still flawed.

Want structured learning?

Take the full Reliability Engineering (SRE) course →