Kubernetes Init Containers Explained
Init containers run to completion before a pod's main containers start, making them the standard way to handle setup steps and startup ordering.
An init container is a container in a Kubernetes pod that runs to completion before any of the pod’s regular containers start. If a pod has multiple init containers, they run one at a time, in order, and each must finish successfully before the next begins — only once every init container has exited cleanly does Kubernetes start the pod’s main containers.
What problem they solve
A pod’s main containers are meant to run continuously, restarting on failure. But plenty of real setup work is a one-shot task: pulling down a configuration file, running a database migration, waiting for a dependency to become reachable, or setting file permissions on a mounted volume before the application touches it. Cramming that logic into the main container’s entrypoint script works, but it mixes one-time setup with the long-running process, complicates restarts, and often requires bundling extra tools into an image that otherwise wants to stay minimal.
Init containers separate the two concerns cleanly. They’re defined the same way as regular containers — same image and command fields — but declared under initContainers in the pod spec instead of containers:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ["sh", "-c", "until nc -z db-service 5432; do sleep 2; done"]
containers:
- name: app
image: my-app:latest
Here, wait-for-db blocks until the database is reachable. The main app container never even starts until that check succeeds — no retry loop needed inside the application itself.
Key properties
- Sequential, not parallel. Multiple init containers run one after another, never concurrently. This matters if setup steps depend on each other’s output.
- Run to completion. Each must exit with status 0 to be considered successful. If one fails, Kubernetes restarts the pod according to its
restartPolicy, re-running the init containers from the start. - Separate resource limits. Init containers can specify their own CPU and memory requests, independent of the main containers, since they typically need different (often smaller) resources for a short-lived task.
- Share the pod’s volumes. An init container can write to a volume that a main container later reads from — a common pattern for fetching configuration or seeding data before the application starts.
- No readiness or liveness probes. Since they’re expected to exit, not run indefinitely, readiness and liveness probes don’t apply to them the way they do to main containers.
Init containers vs sidecar containers
These two patterns are often confused because both add extra containers to a pod, but they solve opposite problems:
| Init containers | Sidecar containers | |
|---|---|---|
| Lifecycle | Run once, to completion, before main containers start | Run alongside main containers for the pod’s whole lifetime |
| Execution | Sequential, one at a time | Concurrent with the main container |
| Typical use | Setup, migrations, waiting on dependencies | Logging agents, proxies, metric exporters |
| When it “finishes” | Must exit successfully to let the pod proceed | Expected to keep running; exiting early is usually a failure |
See the sidecar pattern for the container-that-runs-alongside case. A single pod can use both: an init container to prepare a shared volume, and a sidecar to continuously tail logs from it once the main application is running.
Common use cases
- Waiting for a dependency. Blocking until a database, message queue, or upstream service accepts connections, so the main container doesn’t crash-loop on startup while a dependency is still booting.
- Populating a shared volume. Cloning a git repository, downloading assets, or generating a configuration file that the main container then mounts read-only.
- Registering with external systems. Running a one-time registration or credential-fetch step against an external API before the application needs to authenticate.
- Permission and ownership fixes. Adjusting file ownership on a mounted volume (a common need with persistent volumes) before a non-root main container tries to write to it.
These setup steps sit in the same lifecycle stage as the broader pod scheduling and startup process described in pods, deployments, and services — init containers just insert an ordered, blocking step before the “main containers start” part of that lifecycle.
Debugging init container failures
When a pod is stuck in Init status, kubectl describe pod <name> shows which init container is currently running or has failed, and kubectl logs <pod> -c <init-container-name> retrieves its output — the -c flag is necessary because kubectl logs defaults to a pod’s main container. A pod that never leaves the Init:0/N state almost always means the first init container is failing or blocking indefinitely, which is often the intended behavior for a dependency check that hasn’t succeeded yet.
The takeaway
Init containers give a pod an ordered, blocking setup phase that runs before its main containers start — one container at a time, each required to exit successfully. They’re the standard way to handle startup dependencies, one-time configuration, and volume preparation without baking that logic into a long-running application container, and they’re a different tool from sidecars, which run continuously rather than to completion.
Tagged
Keep reading
Chisato · · 4 min read Kubernetes Service Types: ClusterIP vs NodePort vs LoadBalancer
Kubernetes offers four Service types for exposing pods on the network. How ClusterIP, NodePort, LoadBalancer, and ExternalName each route traffic.
Chisato · · 4 min read Helm vs Kustomize for Kubernetes Config
Helm templates and packages Kubernetes manifests with a templating language; Kustomize patches plain YAML declaratively, with no templates at all.
The Lycoris Team · · 5 min read What Are Kubernetes CRDs? Custom Resources Explained
A Kubernetes CRD (CustomResourceDefinition) extends the API with new resource types, letting the cluster manage custom objects like native ones.