⚡ ~/naveed Labs
⚡ Portfolio Home ✍️ Engineering Blog Deep Dives 🎯 Interview Hub 1,000+ Scenarios ☸️ Kubernetes Mastery Hub 24 Modules 🎮 DevOps Arcade & Quizzes Subnet Blitz ⚡ 🗺️ DevOps Roadmaps PDFs & Guides 🤖 Morpheus Analysis AI Quant ↗ 🛠️ Developer Tools Utilities 🧪 Labs & Experiments 📄 Interactive CV & Certs 🔗 All Links & Socials ⚡ Join The Dispatch (Weekly SRE Newsletter) →
⚡ DevOps Lab RUNBOOK
DevOps Lab / Tutorials / Top 7 Kubernetes Troubleshooting Use Cases
🚨 Production Incident Runbook

Top 7 Kubernetes Troubleshooting Use Cases: Hands-On Incident Runbook

Target: Kubernetes v1.28–v1.32+ · containerd v1.7+ · Linux Kernel 5.15+ · Author: Naveed Ahmed

When an alert fires in the middle of a release or high-traffic peak, production Kubernetes troubleshooting requires precision. This hands-on lab covers the top 7 Kubernetes troubleshooting failure modes with forensic CLI workflows, kernel and container runtime internals, copy-paste triage commands, and permanent remediation manifests.

1
How To Fix Kubernetes Node Not Ready

A node transitions into NotReady when the Kubelet stops updating its heartbeat Lease object with the Kubernetes control plane or when the node's Ready condition evaluates to False. Once the node-monitor-grace-period (default 40s) expires, the Node Controller marks the node unready; after node-eviction-timeout, pods on that node begin evicting.

Forensic Diagnostic Flow

Execute triage commands to isolate the failure domain:

# Step 1: Immediately cordon to protect cluster capacity
kubectl cordon <node-name>

# Step 2: Check conditions and lease heartbeat
kubectl describe node <node-name> | grep -A 10 Conditions:
kubectl get lease -n kube-node-lease <node-name> -o yaml

# Step 3: SSH / SSM into worker node and inspect Kubelet & Containerd
systemctl status kubelet
journalctl -u kubelet -n 100 --no-pager

# Step 4: Validate CRI socket responsiveness
crictl info
crictl ps

# Step 5: Check client TLS certificate expiration
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
Key Root Cause Vectors:
  • Cgroup Driver Mismatch: Kubelet configured with cgroupfs while containerd runs SystemdCgroup = true.
  • Containerd Deadlock: High I/O wait or snapshotter panic locks /run/containerd/containerd.sock.
  • CNI Daemon Partition: AWS VPC CNI (aws-node) runs out of available secondary IPs or subnet CIDR exhaustion occurs.
  • PID / Inode Starvation: OS PID exhaustion prevents systemd from forking health checks.

Permanent Solution & Cluster Return

# Restart containerd and kubelet if deadlocked
systemctl restart containerd && systemctl restart kubelet

# Wait 30 seconds and verify status flips to Ready
kubectl get node <node-name>

# Uncordon the node to resume scheduling
kubectl uncordon <node-name>
🎯 Interview Scenario Deep Dive: Master how to explain Node Not Ready in senior SRE interviews using the STAR framework.
Read Interview Scenario →
2
How to Fix Kubernetes Node Disk Pressure

When available disk drops below configured eviction thresholds (default: imagefs.available < 15% or nodefs.available < 10%), Kubelet flags DiskPressure=True, halts pod scheduling, and immediately starts evicting BestEffort and Burstable pods.

Filesystem Partition Forensics (nodefs vs imagefs)

Never run rm -rf on containerd storage layers. Identify the saturated partition first:

# On the worker node: check mount usage
df -hT / /var/lib/containerd /var/log

# If imagefs is full: safely prune unreferenced images
crictl rmi --prune

# If nodefs is full: identify runaway container logs in /var/log/pods
du -sh /var/log/pods/* | sort -hr | head -n 10

# Safely truncate runaway log file without breaking open file descriptors
truncate -s 0 /var/log/pods/<namespace_pod_uid>/<container>/*.log

# Check for bloated emptyDir volumes
du -sh /var/lib/kubelet/pods/*/volumes/kubernetes.io~empty-dir/* | sort -hr | head -n 10

Permanent Kubelet Garbage Collection Configuration

In /var/lib/kubelet/config.yaml, enforce proactive automated garbage collection:

imageGCHighThresholdPercent: 80
imageGCLowThresholdPercent: 65
imageMinimumGCAge: 2m
containerLogMaxSize: 50Mi
containerLogMaxFiles: 5
evictionHard:
  nodefs.available: 10%
  imagefs.available: 15%
evictionSoft:
  nodefs.available: 15%
  imagefs.available: 20%
evictionSoftGracePeriod:
  nodefs.available: 2m
  imagefs.available: 2m
🎯 Interview Scenario Deep Dive: Review senior SRE questions on Kubelet QoS eviction hierarchy and image garbage collection.
Read Interview Scenario →
3
Understanding Kubernetes CreateContainerError

In CreateContainerError, the container image was pulled successfully, but when containerd or CRI-O calls the Linux kernel to create the container sandbox (attaching mounts, setting up namespaces, configuring cgroups, or enforcing AppArmor/SELinux profiles), the system call fails. Because application code never ran, kubectl logs returns nothing.

Extracting the Container Runtime Error

# Step 1: Capture the exact CRI RPC failure message
kubectl describe pod <pod-name> | grep -A 8 'State: Waiting'

# Step 2: Audit stuck PersistentVolume attachments (EBS Multi-Attach trap)
kubectl get volumeattachment | grep <pv-name>

# Step 3: Check host kernel audit logs for AppArmor / SELinux rejections
dmesg -T | grep -iE 'apparmor|selinux|audit|denied'

# Step 4: Check host inotify watches limit
cat /proc/sys/fs/inotify/max_user_watches

Permanent Solution & Fixes

1. EBS Multi-Attach Deadlock: If an EBS volume is locked to a crashed worker node, force detach the AWS volume or delete the stale VolumeAttachment object.
2. Missing subPath Directory: If using subPath on an empty volume, initialize the folder using an initContainer with proper directory permissions.
3. Inotify Limits: Permanently increase limits in /etc/sysctl.d/99-kubernetes.conf: fs.inotify.max_user_watches = 524288.
🎯 Interview Scenario Deep Dive: How to explain CRI runtime sandbox failures vs storage deadlocks in architecture interviews.
Read Interview Scenario →
4
Understanding Kubernetes CreateContainerConfigError

CreateContainerConfigError happens earlier than CreateContainerError. Before Kubelet even touches the container runtime, it must assemble environment variables and projections from API server objects. If a referenced ConfigMap or Secret is missing, or a key does not exist inside it, Kubelet stops immediately with this error.

Triage Workflow & Diagnosis

# Describe pod to reveal the exact missing resource name and key
kubectl describe pod <pod-name>

# Typical output:
# Error: configmap "api-config" not found
# Error: couldn't find key DB_PASSWORD in Secret default/db-credentials

# Check existing ConfigMaps and Secrets in the workload namespace
kubectl get configmap,secret -n <namespace>

# Check External Secrets Operator (ESO) synchronization health
kubectl get externalsecret -n <namespace>
kubectl describe externalsecret <name> -n <namespace>

The optional: true Resilience Pattern

Prevent pod startup crashes for non-essential configuration variables using optional: true:

env:
  - name: FEATURE_TOGGLES
    valueFrom:
      configMapKeyRef:
        name: app-toggles
        key: feature_flags
        optional: true   # Pod starts gracefully even if ConfigMap is absent!
envFrom:
  - configMapRef:
      name: non-critical-tuning
      optional: true
🎯 Interview Scenario Deep Dive: Triage GitOps sync order race conditions and ArgoCD sync waves in senior engineering screens.
Read Interview Scenario →
5
Understanding Kubernetes ImagePullBackOff & ErrImagePull

ErrImagePull is the initial error returned when Kubelet fails to fetch a container image layer or manifest. Kubernetes then places the pod into ImagePullBackOff, enforcing an exponential backoff retry loop (5s, 10s, 20s... capped at 5 minutes).

Decoding the 4 Primary Root Causes

# Step 1: Decode exact RPC error message from describe events
kubectl describe pod <pod-name> | grep Events -A 10

# Error 1: 404 Not Found / manifest unknown -> Typo in image repository or tag
# Error 2: 401 Unauthorized / 403 Forbidden -> Missing/expired registry credentials
# Error 3: 429 Too Many Requests -> Docker Hub rate limit reached (100 pulls/6hr)
# Error 4: i/o timeout -> Private VPC subnet missing NAT Gateway or ECR Endpoints
# Error 5: no match for platform in manifest -> ARM64 vs AMD64 architecture mismatch

# Step 2: Test image pull directly on the worker node using crictl
crictl pull <image-name>:<tag>

Private Subnet AWS VPC & S3 Gateway Endpoint Fix

In air-gapped or private AWS VPC subnets, AWS ECR requires both interface endpoints AND an Amazon S3 Gateway Endpoint to download layer blobs:

# Verify VPC Endpoints in AWS CLI
aws ec2 describe-vpc-endpoints --filters "Name=vpc-id,Values=<vpc-id>"   --query "VpcEndpoints[*].[ServiceName,VpcEndpointType,State]" --output table

# Required Endpoints for ECR:
# 1. com.amazonaws.<region>.ecr.api (Interface)
# 2. com.amazonaws.<region>.ecr.dkr (Interface)
# 3. com.amazonaws.<region>.s3      (Gateway - CRITICAL: layer blobs live in S3!)
🎯 Interview Scenario Deep Dive: Master ImagePullBackOff, ECR IRSA IAM tokens, and Docker Buildx multi-arch pipeline design.
Read Interview Scenario →
6
Understanding Kubernetes RunContainerError

Unlike CrashLoopBackOff (where the application started and crashed), in RunContainerError the operating system kernel failed to start the container task (execve(2) system call failed) or a postStart lifecycle hook failed with a non-zero exit code.

Diagnostic Triage Checklist

# Step 1: Inspect event logs for kernel execve failure
kubectl describe pod <pod-name> | grep Events -A 10

# Symptom A: "exec: "/entrypoint.sh": stat /entrypoint.sh: no such file or directory"
# Root Cause: Windows CRLF line endings (
) in shebang line (#!/bin/sh
)
# Fix: Run dos2unix entrypoint.sh before building Dockerfile

# Symptom B: "exec: "/bin/bash": stat /bin/bash: no such file or directory"
# Root Cause: Distroless or scratch base image has no shell!
# Fix: Execute binary directly: command: ["/app/server"]

# Symptom C: "permission denied"
# Root Cause: Missing executable bit or runAsNonRoot: true with root-owned binary
# Fix: RUN chmod +x /entrypoint.sh in Dockerfile

# Symptom D: "FailedPostStartHook"
# Root Cause: A lifecycle.postStart exec command exited with non-zero code.
# Kubernetes contract: If postStart fails, the container is killed instantly!
🎯 Interview Scenario Deep Dive: Explain kernel execution boundaries, Distroless images, and postStart hooks to hiring managers.
Read Interview Scenario →
7
Understanding Kubernetes OOMKilled (Exit Code 137)

Exit Code 137 is 128 + 9 (SIGKILL). It indicates that the process was unconditionally terminated by the Linux kernel Out-Of-Memory killer. A container is OOMKilled when its memory working set crosses the Linux cgroup limit (memory.max in cgroups v2 or memory.limit_in_bytes in cgroups v1), even if the host node has 64GB of free RAM.

Prometheus Working Set vs Process RSS

Kubernetes does NOT evaluate Resident Set Size (RSS) alone—it tracks container_memory_working_set_bytes, which includes active page cache:

working_set = memory.current - inactive_file
# PromQL Query to detect containers nearing OOM limit
(container_memory_working_set_bytes{container!=""} /
 kube_pod_container_resource_limits{resource="memory"}) * 100 > 85

# Differentiate Container OOM from Node OOM
kubectl describe pod <pod-name>   # Look for Last State: Terminated, Exit Code 137
kubectl describe node <node-name>  # Look for MemoryPressure: True/False

JVM / Java Container Memory Blindness Solution

Older JVMs allocate heap based on host physical RAM instead of container cgroup limits. Configure modern container-aware JVM flags:

# Recommended production JVM flags in Kubernetes
JAVA_OPTS="-XX:+UseContainerSupport            -XX:MaxRAMPercentage=75.0            -XX:InitialRAMPercentage=50.0            -XX:+ExitOnOutOfMemoryError"
🎯 Interview Scenario Deep Dive: Master cgroups memory accounting, Kubelet QoS oom_score_adj tiers, and JVM tuning.
Read Interview Scenario →
Live Incident Simulator

Practice Real Incidents in Kubectl Crash Simulator

Troubleshoot crashing pods, broken services, and failing nodes under a ticking 60-second game clock.

Play Crash Simulator 🎮