koti.dev
← The Runbook
Mastering Kubernetes the Right Way · DAY 01 / 35

RunContainerError in Kubernetes: Why Your Pod Never Starts

The kubelet pulled the image, runc tried to exec, and nothing happened. Here is the one line that tells you why.

KV
Koti Vellanki20 Mar 20266 min read
kubernetesdebuggingapplication
RunContainerError in Kubernetes: Why Your Pod Never Starts

2:07 AM. A Helm chart update just rolled to production and every new pod is stuck at RunContainerError. My manager is typing in Slack. The image pulled fine, the node has capacity, the previous revision is still serving traffic but the HPA is about to scale it and I am one event away from a real outage. I run kubectl logs and get nothing because the container never produced a single byte of stdout. The status column says four words. No stack trace. No hint. Just a pod that refuses to exist, yaar, and a clock that is not on my side.

The scenario

This one comes from my open-source repo troubleshoot-kubernetes-like-a-pro. You are going to reproduce the exact failure on your own cluster so the next time you see RunContainerError at 2 AM, your muscle memory takes over.

bash
git clone https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro.git cd troubleshoot-kubernetes-like-a-pro/scenarios/wrong-container-command ls

You will see three files: description.md with the background, issue.yaml with the broken pod, and fix.yaml with the working version. This assumes you already have a cluster running from Day 0, cloud or local, it does not matter.

Reproduce the issue

Apply the broken manifest.

bash
kubectl apply -f issue.yaml kubectl get pods
plaintext
NAME READY STATUS RESTARTS AGE wrong-container-command-pod 0/1 RunContainerError 2 38s

There it is. RunContainerError, two restarts, the pod is burning cycles and telling you absolutely nothing about why.

Terminal: git clone the troubleshoot repo, cd into scenarios/wrong-container-command, ls the folder, kubectl apply -f issue.yaml, kubectl get po showing wrong-container-command-pod with RunContainerError status
Clone the repo, apply issue.yaml, and kubectl get po confirms the pod is in RunContainerError with the restart counter already climbing.

Debug the hard way

First instinct, logs.

bash
kubectl logs wrong-container-command-pod
plaintext
Error from server (BadRequest): container "busybox" in pod "wrong-container-command-pod" is waiting to start: StartError

Empty. The container never reached a running state so there is no stdout to read. Juniors retry this command three or four times hoping it changes. It never will.

Try describe, scroll past volumes and conditions and tolerations until you hit the events block.

bash
kubectl describe pod wrong-container-command-pod
plaintext
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Pulled 30s (x3 over 45s) kubelet Successfully pulled image "busybox" Warning Failed 29s (x3 over 45s) kubelet Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "invalid-command-that-does-not-exist": executable file not found in $PATH

Read that carefully. Pulled is Normal, so the image is fine. Failed is where the truth lives. The last line is the whole story: executable file not found in $PATH. The kubelet did its job, containerd did its job, runc tried to exec the command and the binary was not there.

Terminal: kubectl describe pod wrong-container-command-pod output showing the pod spec, container state Waiting with reason RunContainerError, and the runc exec error about invalid-command-that-does-not-exist
kubectl describe pod — the top half shows the container state stuck in Waiting, reason RunContainerError, and the runc exec error calling out the missing binary.
Terminal: bottom half of kubectl describe pod showing the Events table with repeated Normal Pulled, Normal Created, Warning Failed with the runc exec error, and Warning BackOff entries
Scroll to the Events table. Pulled is Normal, Created is Normal, Failed is where the truth lives: exec format error on the missing binary, then BackOff as the kubelet gives up and retries.

Why this happens

When you set command: in a pod spec, you override the image's ENTRYPOINT completely. The kubelet hands the pod to the container runtime, containerd creates a task, and runc does an execve with the first element of your command: array. If that path does not resolve inside the container's filesystem, execve returns ENOENT and the container never becomes a running process. There is no stdout because there is no process. There are no application logs because there is no application.

The pod is not broken at the Kubernetes level. It is broken at the Linux level, one layer below where most people look.

The fix

Apply the fixed manifest.

bash
kubectl apply -f fix.yaml kubectl get pods

The key change is in the command: array. The broken version was pointing at a binary that does not exist in the busybox image:

yaml
command: - "sh" - "-c" - "echo 'Wrong container command fixed' && sleep 1000"

sh exists. It is in /bin/sh. runc finds it, execs it, the container enters the running state.

Terminal: cat issue.yaml and cat fix.yaml showing the diff between invalid-command-that-does-not-exist and the sh -c echo + sleep command, followed by kubectl apply -f fix.yaml creating wrong-container-command-fixed-pod
Diff the two manifests side by side. The broken spec calls an invalid binary; the fix uses sh -c with a command that actually exists in the busybox image.
plaintext
NAME READY STATUS RESTARTS AGE wrong-container-command-fixed-pod 1/1 Running 0 4s
Terminal: kubectl get po showing two pods side by side — wrong-container-command-fixed-pod with status Running, READY 1/1, RESTARTS 0 and the original wrong-container-command-pod still in CrashLoopBackOff with 7 restarts
The fixed pod is Running and ready after a single apply. The original broken pod is still crash-looping until you delete it — proof the fix is isolated to the new manifest.

The easiest way — with Kubilitics

Open the Pods view. The failing pod is already badged with its status and the failure counter is front and centre.

Kubilitics desktop app Pods view showing wrong-container-command-pod with a CrashLoopBackOff status badge, 1 total pod, 1 failed, READY column 0/1, RESTARTS counter visible
Kubilitics Pods view — the broken pod is badged CrashLoopBackOff with the ready count and restart counter visible without opening anything.

Click the pod and jump to the Events tab. Kubilitics groups the kubelet events by severity and collapses the noise, so the Warning line about the missing binary sits at the top of the list instead of buried in a wall of Normal Pulled events.

Kubilitics Events tab for the failing pod showing BackOff warning events with timestamps, kind, source, severity, event count, and the specific message about back-off restarting failed container busybox
Events tab — the BackOff warnings are grouped, timestamped, and severity-colored. No grep, no scroll, the failure signal is the first thing you see.

The Overview tab shows the full pod spec with Annotations and Tolerations expanded. The bad command: array is right there in the rendered YAML, one click away from confirming the root cause without leaving the app.

Kubilitics Overview tab showing the pod's Tolerations table and Annotations section with the full pod spec JSON including the command field set to invalid-command-that-does-not-exist
Overview tab — the pod's rendered spec is visible inline. The command field reveals invalid-command-that-does-not-exist immediately, no kubectl get pod -o yaml needed.

After kubectl apply -f fix.yaml, the list refreshes in real time. The fixed pod shows green, the broken one is still badged red, and you can see both outcomes at a glance while deciding whether to delete the old pod.

Kubilitics Pods view after applying the fix showing two pods — wrong-container-command-fixed-pod with green Running status and wrong-container-command-pod still showing CrashLoopBackOff
Kubilitics Pods view after the fix lands — the new pod is green, the original is still red, the restart counter is frozen. Side-by-side visual confirmation that the fix worked.

The lesson

  1. When a pod status is RunContainerError, stop reading logs. Read the Failed event message and look for the runc line.
  2. command: overrides ENTRYPOINT. You own the full exec path. Test it inside the image before you ship the YAML.
  3. Empty logs are a diagnostic signal, not a dead end. If there is no stdout, the container never ran, which means the problem is in the runtime, not the app.

Day 1 of 35 — tomorrow the container does start, several times a minute, and still will not stay alive.

◆ Newsletter

Get the next post in your inbox.

Real Kubernetes lessons from seven years in production. One email when a new post drops. No spam. Unsubscribe in one click.