Init Containers and Completion Order
An init container runs to completion before the next one starts, and all of them finish before any application container begins. That makes them the standard place to wait for a dependency, fetch a file or run a migration - and it makes them the standard reason a Pod sits at 0/1 with a STATUS nobody recognises. Both halves are here: the ordering, and the failure.
Application Design and Build Guide 8 of 44 Beginner
- Kubernetes1.36.4
- Runtimecontainerd 2.2.6
- CNICalico v3.32.1
- TimeAbout 14 min
- Reviewed23 August 2026
Written against the versions above. An init container's `restartPolicy` follows the Pod's. With `restartPolicy: Always` (the default for a bare Pod) a failing init container is retried with backoff forever, which is why the failing Pod below accumulates restarts rather than being marked failed.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA1001 | 192.168.0.175 | Ubuntu 26.04 LTS | Control Plane Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE01 | 192.168.0.176 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE02 | 192.168.0.177 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE03 | 192.168.0.178 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
Before you start
- A cluster and kubectl.
- The session creates namespace
ckad-init, a Podappwith two init containers writing to a sharedemptyDir, and a Podblockedwhose init container looks up a hostname that does not exist.
-
Two init containers, in order
Each writes a line to a shared volume and sleeps five seconds. Watch the STATUS column change as they go -
Init:0/2, thenInit:1/2, thenPodInitializing, thenRunning. The fraction is how many init containers have finished.When the app container finally starts, it reads the file they left:
first secondIn order.
firstcompleted beforesecondstarted - init containers are strictly sequential, unlike application containers which all start together. That is the property you rely on when one has to fetch something the next one needs.bash Example session kubectl create namespace ckad-initnamespace/ckad-init createdsleep 4; kubectl -n ckad-init get pod appNAME READY STATUS RESTARTS AGEapp 0/1 Init:0/2 0 4ssleep 8; kubectl -n ckad-init get pod appNAME READY STATUS RESTARTS AGEapp 1/1 Running 0 13skubectl -n ckad-init wait --for=condition=Ready pod/app --timeout=120spod/app condition metkubectl -n ckad-init logs appDefaulted container "app" out of: app, first (init), second (init)firstsecondExpected resultThe Pod passes through Init states and the app prints both lines in order.
Success conditionYou have seen sequencing you can rely on for a dependency or a migration.
-
The app container did not start until they were done
INIT DONE APP first,second Completed,Completed 2026-08-23T03:05:16ZBoth init containers
Completed, and only then does the app container have astartedAt. This is the guarantee: an application container cannot observe a half-finished init sequence, because it does not exist yet.It is also the cost. Init containers are on the critical path of every Pod start, including every rollout and every restart. A thirty-second init container adds thirty seconds to each of those.
bash Example session kubectl -n ckad-init get pod app -o 'custom-columns=INIT:.status.initContainerStatuses[*].name,DONE:.status.initContainerStatuses[*].state.terminated.reason,APP:.status.containerStatuses[*].state.running.startedAt'INIT DONE APPfirst,second Completed,Completed 2026-08-23T03:05:16ZExpected resultTwo
Completedinit containers and one start time after them.Success conditionYou can prove the ordering from status rather than from timing.
-
One that fails
This init container resolves a Service that was never created. After thirty seconds:
NAME READY STATUS RESTARTS AGE blocked 0/1 Init:Error 2 (29s ago) 30sInit:Error, and the phase is stillPending- notRunning, notFailed. The application container has never been created. Anyone looking for a crash in the app's logs will find nothing at all, because there is nothing there.The restart count is on the *init* container, and it is climbing, because the Pod's default
restartPolicy: Alwaysretries it with backoff indefinitely.The logs are where the actual reason is, and you have to ask for the init container by name:
** server can't find does-not-exist.ckad-init.svc.cluster.local: NXDOMAINbash Example session sleep 30; kubectl -n ckad-init get pod blockedNAME READY STATUS RESTARTS AGEblocked 0/1 Init:Error 2 (29s ago) 30skubectl -n ckad-init get pod blocked -o 'custom-columns=PHASE:.status.phase,INIT:.status.initContainerStatuses[*].state.waiting.reason,RESTARTS:.status.initContainerStatuses[*].restartCount'PHASE INIT RESTARTSPending <none> 2kubectl -n ckad-init logs blocked -c waitfor --tail=6Address: 10.96.0.10:53 ** server can't find does-not-exist.ckad-init.svc.cluster.local: NXDOMAIN ** server can't find does-not-exist.ckad-init.svc.cluster.local: NXDOMAINExpected result
Init:Error, phasePending, restarts climbing, and NXDOMAIN in the logs.Success conditionYou can recognise a Pod that is stuck before it ever started.
-
Where the events point
Normal Scheduled 30s default-scheduler Successfully assigned ckad-init/blocked to cka1001-node01 Normal Pulled 18s (x3 over 30s) kubelet spec.initContainers{waitfor}: Container image "busybox:1.36" already present on machineNote the prefix:
spec.initContainers{waitfor}. Events name which container they are about, and that prefix is the fastest way to tell an init failure from an application failure when you are readingdescribeoutput under time pressure.The
x3is the retry count in event form.The debugging order for a Pod stuck at
Init::kubectl get pod- the fraction says which init containerkubectl logs- the real error-c kubectl describe pod- if the logs are empty, the image or the volume is the problem
bash Example session kubectl -n ckad-init describe pod blocked | grep -A4 "Events:" | head -8Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 30s default-scheduler Successfully assigned ckad-init/blocked to cka1001-node01 Normal Pulled 18s (x3 over 30s) kubelet spec.initContainers{waitfor}: Container image "busybox:1.36" already present on machine and can be accessed by the podkubectl delete namespace ckad-init --wait=falsenamespace "ckad-init" deletedExpected resultEvents prefixed with the init container's name.
Success conditionYou have a three-step routine for an Init failure.
Troubleshooting
A Pod sits at
Init:0/1and the app logs are empty.Why: The application container has not been created yet.
Fix:
kubectl logs <pod> -c <init-container-name>. The init container's name is inkubectl describe.Init:CrashLoopBackOff.Why: The init container keeps exiting non-zero and is being retried with backoff.
Fix:Same lookup - read its logs. A wait-for-dependency init container will do this forever if the dependency never appears.
Rollouts became much slower after adding an init container.
Why: Init containers run on every Pod start, including every rollout.
Fix:Expected. Keep them short, or move the work somewhere that runs once.
The init container's files are not visible to the app.
Why: The volume is not mounted in both.
Fix:Mount the same volume in the init container and the app container - see guide 20.