LearnDevOpsHands-On Labs
Troubleshooting Matrix/Init:CrashLoopBackOff
KubernetesCritical Severity

Init:CrashLoopBackOff

Fix Init:CrashLoopBackOff: run kubectl logs pod -c init-name, then check the dependency it waits on, migrations that cannot rerun, and volume permissions.

Observed Error Signature / Log Pattern
Init:CrashLoopBackOff

Incident Overview

KubernetesKubernetes

Fix Init:CrashLoopBackOff: run kubectl logs pod -c init-name, then check the dependency it waits on, migrations that cannot rerun, and volume permissions.

medium fix·6 min read

By Vishvesh Patel·DevOps Engineer·Updated 21 September 2026

BASHDiagnostic / remediation command
Init:CrashLoopBackOff

Init:Error

Init:0/2

Warning  BackOff  Back-off restarting failed container init-db in pod api-7d4f8c9b6-xk2mn

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

• Read the init container's logs, not the Pod's

kubectl logs without -c returns the main container, which has never started, so you get an unhelpful message about the container not being ready. The init container's name comes from kubectl describe pod.

• See how far the init sequence got

Init containers run strictly in order and each must exit 0 before the next starts. Init:0/2 means the first one has not finished, so only that one is worth looking at.

• Check the previous attempt if it is restarting too fast to catch

With CrashLoopBackOff the current container may not be running when you look. --previous returns the last terminated instance, which is the one that failed.

Init:CrashLoopBackOff means an init container is failing. Init containers run in order, before any app container, and each must exit 0 before the next one starts. One failing init container means the main container never runs at all.

The status tells you where you are:

BASHDiagnostic / remediation command
kubectl logs my-pod -c init-db

Read the right logs

This is where most time gets lost:

That is the main container, which has never started. Name the init container:

--previous matters here. In CrashLoopBackOff the container is often not running when you look, so the current logs are empty and the useful output is in the last terminated instance.

BASHDiagnostic / remediation command
kubectl logs my-pod

Pattern 1: Waiting for a dependency that will never arrive

The most common init container in the wild:

If postgres is not resolvable, this loops forever and the Pod sits in Init:0/1 rather than crashing, which is arguably worse because there is no error to read.

An empty ENDPOINTS means the Service exists and matches no ready Pods, so the name resolves and nothing answers. That is a different fix from the Service not existing at all.

Give the wait a bound so it fails visibly instead of hanging:

Two minutes then a clear failure, with progress in the logs throughout.

Worth asking whether the wait is needed at all. An application that retries its own database connection with backoff handles a restarting database mid-life too, which an init container cannot. The init container only covers the first few seconds of the Pod's existence.

BASHDiagnostic / remediation command
initContainers:
  - name: wait-for-db
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z postgres 5432; do sleep 2; done']

Pattern 2: The migration that cannot be run twice

Init containers re-run on every Pod restart, and with multiple replicas they run concurrently, one per Pod. A migration tool without locking then has several processes racing on the same schema, and all but one fail.

Most tools handle this: Flyway, Liquibase and Rails all take a lock. If yours does not, a Job run once before the rollout is the correct shape, not an init container.

The same applies to anything non-idempotent. mkdir /data/foo fails the second time; mkdir -p /data/foo does not.

BASHDiagnostic / remediation command
initContainers:
  - name: migrate
    image: myapp:1.4.2
    command: ['./migrate', 'up']

Pattern 3: Permissions on a volume

Without runAsUser: 0 the chown fails with Operation not permitted, and a securityContext at Pod level applies to init containers too. A cluster enforcing a restricted Pod Security Standard blocks runAsUser: 0 entirely, so the Pod will not even be admitted.

The better answer where the storage supports it is fsGroup, which lets the kubelet set ownership without a privileged container:

This works for most CSI drivers and removes the init container completely.

BASHDiagnostic / remediation command
initContainers:
  - name: fix-perms
    image: busybox:1.36
    command: ['sh', '-c', 'chown -R 1000:1000 /data']
    volumeMounts:
      - name: data
        mountPath: /data
    securityContext:
      runAsUser: 0

Resources apply differently

The scheduler uses the maximum of any single init container's request and the sum of the app containers' requests, not the total of everything. An init container with a large memory request can therefore make a Pod unschedulable in a way that is not obvious from reading the manifest.

Limits still apply individually, so an init container that exceeds its own memory limit is OOMKilled and you see Init:Error with exit code 137.

BASHDiagnostic / remediation command
kubectl get pod my-pod -o jsonpath='{.status.initContainerStatuses[0].lastState.terminated}' | jq

restartPolicy governs init containers too

With restartPolicy: Always, a failed init container is retried with exponential backoff up to five minutes, hence CrashLoopBackOff. With restartPolicy: Never, which is common for Jobs, the whole Pod goes to Failed on the first init failure and stops. That is why the same broken init container loops forever in a Deployment and fails once in a Job.

A checklist

Frequently Asked Questions

Why does kubectl logs not show my init container's output?

Because kubectl logs defaults to the first app container, which in this situation has never started, so it returns a PodInitializing message instead of anything useful. Pass -c <init-container-name> to select the init container explicitly; the name is in kubectl describe pod under Init Containers. If the Pod is in CrashLoopBackOff the container may not be running at the moment you look, so add --previous to read the last terminated instance, which is the one that actually failed.

What is the difference between Init:Error and Init:CrashLoopBackOff?

Init:Error means the init container exited non-zero, and Init:CrashLoopBackOff means it has done so repeatedly and the kubelet is now delaying each retry with exponential backoff, up to five minutes. They are the same underlying failure at different stages. Which one you end up in depends on the Pod's restartPolicy: Always retries and reaches CrashLoopBackOff, while Never, common in Jobs, marks the Pod Failed after the first failure and stops.

Do init containers run again when a Pod restarts?

Yes. Every init container runs again, in order, each time the Pod starts, including after a node reboot or an eviction and reschedule. That makes them unsuitable for anything not safely repeatable. It also means that with several replicas they run concurrently, one per Pod, so a database migration in an init container has multiple copies racing unless the migration tool takes a lock. A Job run once before the rollout is the right shape for genuinely one-time work.

Why is my Pod stuck at Init:0/1 with no error?

The init container is running and not exiting, which usually means a wait loop whose condition never becomes true, such as until nc -z postgres 5432. Nothing has crashed, so there is no error to read; the Pod simply waits. Check whether the dependency is reachable, and in particular run kubectl get endpoints <service>, since a Service with no ready backends resolves as a name while nothing answers. Always give wait loops a bounded number of attempts and a non-zero exit so the failure is visible.

How do init container resource requests affect scheduling?

The scheduler computes the Pod's effective request as the larger of the maximum single init container request and the sum of the app container requests, because init containers run sequentially and never at the same time as the app containers. A single init container with a large memory request can therefore make the whole Pod unschedulable even when the app containers are small, which is hard to spot from reading the manifest. Limits are still enforced per container, so an init container exceeding its own memory limit is OOMKilled with exit code 137.

Reference and practice

Learn the underlying concept

Other Kubernetes errors

Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.

Get notified about new tutorials and features.

• Roadmaps

• Interview Prep

• Certifications

• Company

© 2026 DevOpsLesson. All rights reserved.

Need to practice this scenario?

Simulate this error condition and practice recovery commands safely in our in-browser terminal.

Open Kubernetes Sandbox