Your main application container sometimes needs things to be ready before it starts — a database must be reachable, a config file must be downloaded, migrations must run. If the main container starts before these are ready, it crashes or behaves incorrectly.

Init Containers solve this. They run to completion before your main container starts. If an init container fails, Kubernetes retries it until it succeeds — the main container never starts until all init containers pass.


How It Works

Pod starts
    |
Init Container 1 runs -> must complete successfully
    |
Init Container 2 runs -> must complete successfully
    |
Main container starts -> only now

Init containers run sequentially, not in parallel. Each one must finish before the next begins. Only after all init containers complete does the main app container start.

This is different from regular containers — init containers are not long-running. They run once, finish their job, and exit.


Common Use Cases

Wait for a service to be ready
  -> init container loops until DB is reachable, then exits
  -> main app starts knowing DB is available

Run database migrations
  -> init container runs migrate command and exits
  -> main app starts on a fully migrated schema

Download config or secrets from external source
  -> init container fetches config file into a shared volume
  -> main app reads the file on startup

Set permissions or prepare directories
  -> init container creates folders, sets ownership
  -> main app has the correct file structure ready

YAML

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      initContainers:
      - name: wait-for-db
        image: busybox
        command: ['sh', '-c', 'until nc -z db-service 5432; do echo waiting for db; sleep 2; done']
        # loops every 2 seconds until port 5432 on db-service is open
        # exits successfully when DB is reachable

      - name: run-migrations
        image: my-app:latest
        command: ['python', 'manage.py', 'migrate']
        # runs DB migrations and exits

      containers:
      - name: my-app
        image: my-app:latest
        ports:
        - containerPort: 8080
        # starts only after both init containers above have completed

Sharing Data Between Init and Main Container

Init containers can write files to a shared volume that the main container reads. This is how you pass downloaded configs or prepared files.

spec:
  volumes:
  - name: shared-data
    emptyDir: {}                    # temporary volume shared between containers

  initContainers:
  - name: download-config
    image: busybox
    command: ['sh', '-c', 'wget -O /shared/config.json <http://config-server/config>']
    volumeMounts:
    - name: shared-data
      mountPath: /shared

  containers:
  - name: my-app
    image: my-app:latest
    volumeMounts:
    - name: shared-data
      mountPath: /app/config        # reads config.json written by init container

Init Container vs Regular Container

Init Container Regular Container
Runs Once, before main container Continuously
Must complete Yes — exits with code 0 No — runs until stopped
On failure Pod retries init container Pod restarts based on restartPolicy
Parallel No — sequential Yes — all run together
Use for Setup, wait, migrate Your actual application