Kubernetes Init Containers with Examples

Tested on Rocky Linux 10.2 (Red Quartz) workstation
Package kubectl 1.36.3
Applies to Any host with kubectl configured; any Kubernetes cluster
Cert prep CKAD
Lab environment Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm
Privilege Normal user (no sudo required on the workstation)
Scope Regular init-container YAML, sequential ordering, shared volumes, status inspection, failure and restart behaviour, idempotency, and resource scheduling. Does not cover native sidecar configuration beyond a brief qualification, probe tuning, Jobs, ConfigMap or Secret depth, PV provisioning, or SecurityContext depth.
Related guides Container command, args, and environment

This article covers regular init containers that run to completion. Kubernetes also defines native sidecars under spec.initContainers using container-level restartPolicy: Always; those continue running with the application and are covered in the Kubernetes sidecar pattern guide.

This walkthrough uses one Pod named web in the init-lab namespace. Two regular init containers prepare a configuration file on a shared emptyDir volume; the application container reads that file after initialization completes.


What Is a Kubernetes Init Container?

A regular init container performs setup work before application containers in the same Pod start.

  • Regular init containers are defined under spec.initContainers
  • They run sequentially in the order listed
  • Each regular init container must complete successfully before the next one starts
  • Application containers start only after all regular init containers succeed

For Pod phases, conditions, and the wider lifecycle, see Kubernetes Pods and Pod Lifecycle.

Regular init containers versus application containers

Regular init container Application container
Runs before application containers Runs after initialization completes
Must normally complete successfully Commonly remains running
Regular init containers execute sequentially Application containers can run concurrently
Used for preparation tasks Runs the main workload
Does not support lifecycle, livenessProbe, readinessProbe, or startupProbe Supports lifecycle handlers and probes

Native sidecars are the exception and can use probes. Probe configuration detail belongs in the Kubernetes health probes and sidecar guides.

Regular init containers versus native sidecars

Kubernetes implements native sidecars as entries under spec.initContainers with container-level restartPolicy: Always. Unlike regular init containers, native sidecars do not run to completion before the application starts. They start in init order and remain running alongside application containers. This behaviour is stable and enabled by default in current Kubernetes.

  • Use a regular init container for one-time setup: configuration, downloads, permission fixes, or in-Pod dependency checks
  • Use a native sidecar for continuous helper work during the Pod lifetime, such as log shipping or proxying

For multi-container Pod YAML, native sidecar lifecycle, probes, startup ordering, and shutdown behaviour, see Kubernetes sidecar pattern.


Create a Pod with Init Containers

Save this manifest as web-init-pod.yaml:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  namespace: init-lab
spec:
  initContainers:
    - name: write-config
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          sleep 5
          echo "app_mode=production" > /work/config.env
          echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> /work/config.env
          echo "write-config done"
      volumeMounts:
        - name: workdir
          mountPath: /work
    - name: verify-config
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          sleep 5
          test -f /work/config.env
          echo "config verified"
      volumeMounts:
        - name: workdir
          mountPath: /work
  containers:
    - name: app
      image: busybox:1.36
      command: ['sh', '-c', 'echo "Application starting" && cat /work/config.env && sleep 3600']
      volumeMounts:
        - name: workdir
          mountPath: /work
  volumes:
    - name: workdir
      emptyDir: {}

The write-config regular init container generates a file on the shared volume. The verify-config init container confirms it exists before app starts.

Create the namespace, then apply the Pod:

bash
kubectl create namespace init-lab

Sample output:

output
namespace/init-lab created
bash
kubectl apply -f web-init-pod.yaml

Sample output:

output
pod/web created

Watch initialization progress:

bash
kubectl get pod web -n init-lab --watch

You should see progression similar to:

output
web   0/1   Init:0/2   0   1s
web   0/1   Init:1/2   0   6s
web   1/1   Running    0   12s

Generated timing will vary. Press Ctrl+C when the Pod reaches Running.

Read init container logs:

bash
kubectl logs web -n init-lab -c write-config

Sample output:

output
write-config done
bash
kubectl logs web -n init-lab -c verify-config

Sample output:

output
config verified

The application log confirms it read the prepared file:

bash
kubectl logs web -n init-lab -c app

Sample output:

output
Application starting
app_mode=production
timestamp=2026-07-26T06:18:24Z

Understand Init Container Behaviour

Sequential ordering

This Pod runs two regular init containers before the application:

  1. write-config creates the required data on the shared volume
  2. verify-config verifies the file exists
  3. app starts only after both succeed

Regular init containers run one at a time. Kubernetes does not start verify-config until write-config exits with code 0. Reordering the list under spec.initContainers changes the initialization sequence.

text
write-config → verify-config → app

Sharing data through a volume

Containers in the same Pod do not share files automatically. Both must mount the same volume. This example uses emptyDir, which survives individual container restarts but is removed when the Pod is deleted. For volume types and persistence concepts, see Kubernetes volumes.

Component Mount Action
write-config workdir at /work Writes config.env
verify-config workdir at /work Confirms the file exists
app workdir at /work Reads the prepared file

The mount paths can differ between containers as long as they reference the same volume name.

Resource requests and scheduling

Regular init containers run sequentially, so Kubernetes does not add all their requests together. For each resource, it compares:

  • the highest request from any regular init container
  • the combined request of the application containers

The larger value becomes the effective Pod request used for scheduling. A short-lived init container with a large CPU or memory request can therefore prevent the Pod from being scheduled on smaller nodes even though that resource is needed only during initialization.

For requests, limits, and QoS detail, see Kubernetes resource requests and limits.

Re-execution and idempotency

Write init-container commands so they are idempotent. Completed regular init containers do not rerun merely because an application container restarts, but the init sequence can run again when the Pod itself or its sandbox is restarted. Deleting and recreating the Pod always runs the initialization sequence again.

The official documentation recommends accounting for possible re-execution and lists Pod-infrastructure restart scenarios that can rerun initialization. A safe file-generation pattern is to write to a temporary file and rename it only after successful completion.

When a regular init container fails, application containers do not start. In these examples, the regular init containers do not define a container-level restart policy, so the kubelet follows the Pod restartPolicy. On Kubernetes versions with ContainerRestartRules enabled, a regular init container can define its own restartPolicy and optional restartPolicyRules, overriding the Pod-level behavior.


Common Init Container Patterns

Pattern Purpose
Generate configuration Create runtime configuration before startup
Copy files Populate a shared volume from a utility image
Wait for a dependency Delay startup until a required endpoint responds
Prepare permissions Adjust shared-volume ownership or mode
Download startup data Fetch assets without adding tools to the application image

The lab write-config init container demonstrates generate configuration. A dependency wait typically uses a short retry loop such as until wget -qO- http://dependency; do sleep 2; done to block Pod startup until an endpoint responds. That controls order inside the Pod; the application should still handle transient failures after startup.


Demonstrate Failure and Restart Behaviour

When a regular init container fails, application containers do not start.

Failure with Always or OnFailure

Save this manifest as fail-init.yaml:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: fail-init
  namespace: init-lab
spec:
  restartPolicy: Always
  initContainers:
    - name: bad-init
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          echo "initialization failed"
          exit 1
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]

Apply it and watch the Pod:

bash
kubectl apply -f fail-init.yaml

Sample output:

output
pod/fail-init created
bash
kubectl get pod fail-init -n init-lab --watch

Press Ctrl+C after you observe Init:CrashLoopBackOff. Otherwise, that command continues watching indefinitely.

Sample output:

output
NAME        READY   STATUS                  RESTARTS   AGE
fail-init   0/1     Init:CrashLoopBackOff   2          25s

For regular init containers with Pod-level restartPolicy: Always, Kubernetes behaves like OnFailure for initialization: a successful init container is not restarted, but a failed one is retried with increasing delay. Status may show Init:Error briefly between retries, then Init:CrashLoopBackOff while the kubelet backs off.

Failure with Never

With restartPolicy: Never, Kubernetes does not retry a non-zero init exit. kubectl get pods may display Init:Error, while the Pod's API phase becomes Failed. Init:Error is a kubectl STATUS value, not a Pod phase.

Save this manifest as fail-init-never.yaml:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: fail-init-never
  namespace: init-lab
spec:
  restartPolicy: Never
  initContainers:
    - name: bad-init
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          echo "initialization failed"
          exit 1
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
bash
kubectl apply -f fail-init-never.yaml

Check both the displayed status and the API phase:

bash
kubectl get pod fail-init-never -n init-lab

Sample output:

output
NAME              READY   STATUS       RESTARTS   AGE
fail-init-never   0/1     Init:Error   0          8s
bash
kubectl get pod fail-init-never -n init-lab -o jsonpath='{.status.phase}{"\n"}'

Sample output:

output
Failed

Job-specific restart behaviour is outside this article.


Inspect Init Container Status and Logs

Check current Pod status:

bash
kubectl get pod web -n init-lab

kubectl get pods may show initialization progress such as:

STATUS Meaning
Init:0/2 Zero of two regular init containers have completed
Init:1/2 One of two regular init containers has completed
Init:Error A regular init container failed
Init:CrashLoopBackOff A failing regular init container is being retried with backoff

These are displayed status reasons from the kubelet, not separate Pod phases. Use kubectl describe pod for init container state, exit codes, and Events.

Inspect init-container state directly from the API:

bash
kubectl get pod web -n init-lab -o jsonpath='{range .status.initContainerStatuses[*]}{.name}{"\t"}{.state.terminated.reason}{"\t"}{.restartCount}{"\n"}{end}'

Sample output:

output
write-config	Completed	0
verify-config	Completed	0

Kubernetes stores init-container state separately under .status.initContainerStatuses.

Read logs from a specific init container:

bash
kubectl logs web -n init-lab -c write-config

After a failed regular init container restarts, read logs from the previous attempt when available:

bash
kubectl logs fail-init -n init-lab -c bad-init --previous

Sample output:

output
initialization failed

Use --previous only for a restarted init container when earlier output is still available.


Change a Regular Init Container Safely

Most fields of spec.initContainers are immutable on an existing Pod. For a regular init container, Kubernetes permits updating the image field, but the change does not restart the Pod or rerun an init container that has already completed. Native sidecar image updates behave differently and are covered in the Kubernetes sidecar pattern guide. To change command, arguments, environment, mounts, or to guarantee that new initialization code runs, recreate the bare Pod. For controller-managed Pods, update the Pod template so the controller creates replacement Pods.

When the Pod is owned by a Deployment, updating the Pod template creates replacement Pods that run the updated initialization sequence. See Deployments and rolling updates for controller-based rollout.


Troubleshoot Common Init Container Problems

Start with describe and logs for the stuck or failing init container:

bash
kubectl describe pod <pod> -n <namespace>
kubectl logs <pod> -n <namespace> -c <init-container>

Include --previous only for a restarted init container.

Symptom Likely cause First check
Stuck at Init:0/N First init failing or waiting Logs and describe for first init
Init:CrashLoopBackOff Non-zero exit from init Init container logs
App missing prepared file Volume or path mismatch Volume name and mountPath in both containers
Wrong setup order Init list order spec.initContainers sequence
App never starts Init not completing All init exit codes in describe

What's Next


References


Summary

Regular Kubernetes init containers run sequential setup work before application containers start. Define them under spec.initContainers, mount a shared volume such as emptyDir, and let the first init container prepare files the application reads later. The lab Pod moved from Init:0/2 to Running only after write-config and verify-config both exited with code 0.

Regular init containers run one at a time in list order, and a failure blocks every application container. Native sidecars under the same field with restartPolicy: Always are a separate pattern covered in the sidecar guide. Check Init:N/M status, .status.initContainerStatuses, init container logs, and describe output before debugging the main application. When init containers live inside a Deployment template, changing the init spec rolls out new Pods that rerun the full initialization chain.


Frequently Asked Questions

1. What is a Kubernetes init container?

A regular init container runs setup work before application containers in the same Pod start. Regular init containers are listed under spec.initContainers, run sequentially, and each must exit successfully before the next regular init container starts. Native sidecars also use spec.initContainers with container-level restartPolicy Always and are covered in the sidecar guide.

2. Can init containers run at the same time?

Regular init containers run one at a time in list order, and each must complete successfully before the next regular init container starts. A native sidecar is also declared under initContainers, but uses container-level restartPolicy Always; after it starts, Kubernetes can continue to the next init container while the sidecar remains running.

3. How do init and application containers share files?

Mount the same volume in both containers. The init container writes to its mount path; the application container reads from its own mount of the same volume. Containers do not share filesystem paths without a shared volume.

4. What happens when an init container fails?

Application containers do not start. The kubelet follows the init container's own restart policy when one is configured; otherwise, it follows the Pod restartPolicy. With an effective policy of Never, kubectl may show Init:Error while the Pod phase becomes Failed.

5. What is the difference between an init container and a sidecar?

A regular init container runs finite preparation work and exits before the application starts. A native sidecar is declared under spec.initContainers with restartPolicy Always and keeps running alongside the application. See the dedicated sidecar guide for lifecycle, probes, and shutdown behaviour.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)