
kubectl
Init:0/2, Init:CrashLoopBackOff, Init:ErrImagePull: the Init: prefix in kubectl's STATUS column
Init:ErrImagePull, Init:CrashLoopBackOff and Init:0/2 all mean an init container is blocking the pod. How the Init: status is built, and how to read it.
Any STATUS that starts with Init: means the pod has not finished running its
init containers, so none of your application containers have started yet. The
part after the colon is about the init container, not your app: Init:0/2 is a
progress count, and Init:CrashLoopBackOff, Init:Error or Init:ErrImagePull
is the reason the first unfinished init container is stuck.
The prefix is there to tell you where to look. ErrImagePull means your app’s
image failed to pull. Init:ErrImagePull means an init container’s image did,
and the app’s image has not even been tried.
The fastest check
List every init container with its current reason, exit code and message:
kubectl get pod <pod> -o jsonpath='{range .status.initContainerStatuses[*]}{.name}{"\t"}{.state.waiting.reason}{.state.terminated.reason}{"\t"}{.state.terminated.exitCode}{"\t"}{.state.waiting.message}{"\n"}{end}'
For a pod whose init image tag has a typo:
migrate ImagePullBackOff Back-off pulling image "busybox:1.36-typo": ErrImagePull: rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/busybox:1.36-typo": failed to resolve reference "docker.io/library/busybox:1.36-typo": docker.io/library/busybox:1.36-typo: not found
For a pod whose first init container exits 1:
wait-for-db Error 1
migrate PodInitializing
The first line that is not a clean exit is the one kubectl reported. Everything
after it reads PodInitializing, which for an init container just means “not my
turn yet”.
Every Init: status, reproduced
All of these were produced on a Kubernetes 1.36.1 kind cluster with containerd
2.3.1, using busybox init containers in front of an nginx app.
A pod with two slow init containers (sleep 20 each), watched from creation:
kubectl get pod slow-init -w
NAME READY STATUS RESTARTS AGE
slow-init 0/1 Init:0/2 0 1s
slow-init 0/1 Init:1/2 0 21s
slow-init 0/1 PodInitializing 0 43s
slow-init 1/1 Running 0 43s
A pod whose first init container exits 1, with the default restartPolicy: Always:
NAME READY STATUS RESTARTS AGE
init-fails 0/1 Init:Error 1 (1s ago) 2s
init-fails 0/1 Init:CrashLoopBackOff 1 (14s ago) 15s
init-fails 0/1 Init:0/2 2 (14s ago) 15s
init-fails 0/1 Init:Error 2 (15s ago) 16s
init-fails 0/1 Init:CrashLoopBackOff 2 (24s ago) 39s
It cycles: Init:Error when the container exits, Init:CrashLoopBackOff while
the kubelet waits out the backoff, and Init:0/2 for the moment it is running
again. The count never moves past 0, because the kubelet reruns the same
container rather than moving on.
The same failure with restartPolicy: Never stops at Init:Error for good, and
the pod’s phase becomes Failed. There is no retry to wait for.
A missing ConfigMap referenced from an init container’s env:
NAME READY STATUS RESTARTS AGE
init-missing-cm 0/1 Init:CreateContainerConfigError 0 8s
Any waiting reason can appear after the prefix. The message behind that one is
configmap "db-config" not found, exactly as it would be for an app container.
Init:ErrImagePull and Init:ImagePullBackOff
This is the one people search for, and it is the plainest of the lot: the image named by an init container could not be pulled. Watched from creation:
NAME READY STATUS RESTARTS AGE
init-bad-image 0/1 Init:0/1 0 0s
init-bad-image 0/1 Init:ErrImagePull 0 4s
init-bad-image 0/1 Init:ImagePullBackOff 0 17s
init-bad-image 0/1 Init:ErrImagePull 0 32s
init-bad-image 0/1 Init:ImagePullBackOff 0 46s
Init:ErrImagePull is the attempt that just failed; Init:ImagePullBackOff is
the wait before the next one. The events carry the reason, and the reason is the
same list as for any other image: wrong tag, missing registry credentials, private
image, wrong platform, rate limit.
kubectl events --for pod/init-bad-image
LAST SEEN TYPE REASON OBJECT MESSAGE
28s Warning Failed Pod/init-bad-image Failed to pull image "busybox:1.36-typo": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/busybox:1.36-typo": failed to resolve reference "docker.io/library/busybox:1.36-typo": docker.io/library/busybox:1.36-typo: not found
28s Warning Failed Pod/init-bad-image Error: ErrImagePull
27s Normal BackOff Pod/init-bad-image Back-off pulling image "busybox:1.36-typo"
27s Warning Failed Pod/init-bad-image Error: ImagePullBackOff
13s (x2 over 4m26s) Normal Pulling Pod/init-bad-image Pulling image "busybox:1.36-typo"
Everything in ImagePullBackOff and ErrImagePull applies unchanged, including how to read each registry message and fix it. The only init-specific points are these:
- Init containers are often the image nobody looks at. They tend to come from
a Helm chart’s defaults (
busybox, await-forimage, a migration tool) and point at Docker Hub while your app points at a private registry. A pod whoseimagePullSecretscover the app’s registry can still fail on the init image. - A slow init pull does not show as a pull. While the image is still
downloading, the init container’s waiting reason is
PodInitializing, and kubectl deliberately skips that reason, so STATUS readsInit:0/1. On a node pulling something large, a pod can sit atInit:0/1for minutes with nothing wrong.kubectl eventswill showPullingwith noFailedafter it. - Fixing it does not need a new pod. The kubelet keeps retrying, with the backoff capped at five minutes, so correcting the tag in a Deployment rolls a new pod anyway, and fixing the credentials lets a bare pod recover on the next retry.
How kubectl builds the Init: string
kubectl get pods does not compute STATUS itself. It asks the API server for a
Table (Accept: application/json;as=Table;v=v1;g=meta.k8s.io at -v=8), and
the API server fills the STATUS cell by running printPod in
pkg/printers/internalversion/printers.go. So the rules that follow are your API
server’s version, not your kubectl’s. The function is identical on release-1.36
and release-1.37, 43 lines further down on 1.37; line numbers below are
1.36 / 1.37.
It walks status.initContainerStatuses in order, and stops at the first one that
is not finished (lines 965 / 1008):
- Terminated with exit code 0: done, move to the next.
- A sidecar that has started: also counts as done (986 / 1029). More below.
- Terminated non-zero:
Init:plus the terminated reason, which with containerd is usuallyInit:Error(1001 / 1044). If the runtime supplied no reason, it falls back toInit:Signal:NorInit:ExitCode:N(996–998 / 1039–1041). - Waiting with any reason except
PodInitializing:Init:plus that reason, which is whereInit:CrashLoopBackOff,Init:ErrImagePullandInit:CreateContainerConfigErrorcome from (1004–1005 / 1047–1048). - Anything else, meaning running or waiting its turn:
Init:N/M, where N is the position of this container and M islen(pod.Spec.InitContainers)(1008 / 1051).
Only if that walk finished cleanly, or the pod’s Initialized condition is
already true, does it go on to look at app containers (1014 / 1057). That is
where the un-prefixed PodInitializing comes from: the init containers are all
done, and the app container’s waiting reason is PodInitializing because it has
not been created yet. In the slow-init pod above, it lasted as long as the
nginx pull.
Reading init container logs
A plain kubectl logs goes to the app container, which does not exist yet:
kubectl logs init-fails
Defaulted container "app" out of: app, wait-for-db (init), migrate (init)
Error from server (BadRequest): container "app" in pod "init-fails" is waiting to start: PodInitializing
The first line lists the init containers for you. Name one with -c:
kubectl logs init-fails -c wait-for-db
connecting to db:5432
nc: bad address db
That already shows the most recent failed run, even while the pod reads
Init:Error or Init:CrashLoopBackOff, because the last exited instance is still
on the node. --previous asks for the run before that:
kubectl logs init-fails -c wait-for-db --previous
It worked while the pod was in Init:CrashLoopBackOff, and printed the same two
lines. Asked while the pod read Init:Error, the same command returned:
unable to retrieve container logs for containerd://db8ea4e72ff82d88b27b5f38a08f3acca751195886de0fa3529e60951c7ab6bd
That is the kubelet tidying up. pruneInitContainersBeforeStart in
pkg/kubelet/kuberuntime/kuberuntime_container.go (line 972 on release-1.36,
974 on release-1.37) keeps only the last exited instance of each init container
and removes the rest, and it runs on every pod sync. For init containers, reach
for -c <name> first; --previous often points at a container that is already
gone.
kubectl logs <pod> --all-containers also includes init containers, in order,
which is quicker when you do not know which one failed.
Sidecars change the count
A native sidecar is an init container with restartPolicy: Always. It is stable
since Kubernetes 1.33 and starts in init order but keeps running alongside the app.
A pod with a sidecar and one ordinary init container (sleep 30):
initContainers:
- name: log-shipper
image: busybox:1.36
restartPolicy: Always
command: ["sh", "-c", "while true; do sleep 3600; done"]
- name: migrate
image: busybox:1.36
command: ["sh", "-c", "echo migrating; sleep 30; echo done"]
NAME READY STATUS RESTARTS AGE
with-sidecar 0/2 Init:0/2 0 0s
with-sidecar 1/2 Init:1/2 0 1s
with-sidecar 1/2 PodInitializing 0 31s
with-sidecar 2/2 Running 0 31s
Three things differ from a pod without one:
- The sidecar counts in M.
Init:0/2here means one sidecar and one ordinary init container, because M is every entry ininitContainers. - The sidecar counts as done once it has started, not once it exits, which it never will. That is how N reached 1 after one second.
- READY includes it. The denominator is 2 with one app container:
printPodadds each restartable init container to the total (959–960 / 1002–1003), and counts it as ready when it is (988 / 1031). A pod reading1/2with a single app container is showing you its sidecar.
The surprising part comes after initialisation. This sidecar exits 1 every 15 seconds, next to an app that runs fine:
NAME READY STATUS RESTARTS AGE
flaky-sidecar 1/2 Init:Error 0 16s
flaky-sidecar 2/2 Running 1 (0s ago) 16s
flaky-sidecar 1/2 Init:Error 1 (16s ago) 32s
flaky-sidecar 1/2 Init:CrashLoopBackOff 1 (12s ago) 43s
flaky-sidecar 2/2 Running 2 (12s ago) 43s
The pod is initialised (Initialized=True) and the app never stopped, yet STATUS
says Init:CrashLoopBackOff. The init walk sets Init:Error for the sidecar
because it is no longer started, and the app-container pass that follows only
overwrites the reason for containers that are waiting or terminated (1030 / 1073);
a healthy running app leaves it alone. So on a running pod, an Init: status
can be a sidecar crash-looping, not initialisation failing. It is not harmless
either: with the sidecar down, the pod’s Ready condition was False, so it
drops out of Service endpoints.
Sidecars also get different validation. A startupProbe on an ordinary init
container is rejected outright:
The Pod "probe-on-init" is invalid: spec.initContainers[0].startupProbe: Forbidden: may not be set for init containers without restartPolicy=Always
Probes only make sense on something that keeps running, so they are allowed on
sidecars and forbidden on everything else in initContainers.
Fixing it
Init:ErrImagePull/Init:ImagePullBackOff: read theFailedevent, then fix the image reference or the pull secret. Covered in full in the ImagePullBackOff guide.Init:Error/Init:CrashLoopBackOff:kubectl logs <pod> -c <init>. The usual cause is a dependency the init container waits for (a database, a DNS name, a migration lock) and gives up on. The exit code reads the same as for an app container; see CrashLoopBackOff.Init:CreateContainerConfigError: the message names the missing ConfigMap or Secret. Init containers resolve references in the pod’s own namespace, like everything else.- Stuck at
Init:N/Mwith no error: the init container is running. Check its logs for whatever it is waiting on, andkubectl eventsfor a slow pull. PodInitializingfor a long time: init is done. The app containers are being created, and the usual wait is the app image pull.
Preventing it
- Give waiting init containers a deadline. An init container that loops
forever on “wait for db” leaves the pod at
Init:0/1with no error and no restarts. One that exits non-zero after a bounded wait gives youInit:CrashLoopBackOffand logs, which is far easier to spot. - Pin and mirror init images like app images. Same registry, same pull secret,
a tag rather than
latest. - Remember that sidecars show up as
Init:too. On a pod that is serving traffic,Init:CrashLoopBackOffmeans check the sidecars before the init steps.
For how the rest of the STATUS column is derived, including why Terminating
overrides almost everything, see
how kubectl computes a pod’s STATUS.
KubeGlance is a native Kubernetes client for iPhone and iPad, with a full Mac app on the same core. Its pod list computes STATUS and READY the way the API server does, Init: prefixes and sidecar counts included, so a 1/2 on your phone means what it means in your terminal.
Get KubeGlanceThe Kubernetes dashboard that fits in your pocket
KubeGlance is a native Kubernetes client for iPhone and iPad — the real dashboard, not a companion — with a full Mac app on the same core. Pods, workloads, logs and events, straight from your kubeconfig.
Download KubeGlance

