GitHub EN PT

K8s by Example: Init Containers

Init containers run before app containers start. They execute sequentially and must complete successfully. If any init container fails, the Pod restarts. Use for: migrations, config fetching, dependency checks.

pod-init.yaml

Init containers are defined in initContainers. They run before the main containers array starts. Each init container must exit 0 before the next one runs.

spec:
  initContainers:
    - name: migrations
      image: my-app:v1
      command: ["python", "migrate.py"]
      envFrom:
        - secretRef:
            name: db-secrets
  containers:
    - name: app
      image: my-app:v1
pod-init-sequence.yaml

Init containers run in order. Each must complete (exit 0) before the next starts. All init containers must succeed before app containers begin.

spec:
  initContainers:
    - name: step-1-migrate
      image: my-app:v1
      command: ["./migrate.sh"]
    - name: step-2-seed
      image: my-app:v1
      command: ["./seed.sh"]
    - name: step-3-validate
      image: my-app:v1
      command: ["./validate.sh"]
  containers:
    - name: app
      image: my-app:v1
pod-init-volume.yaml

Init containers can download files, generate config, clone repos, or prepare data before the app starts. Here the init container fetches a config file to a shared volume.

spec:
  initContainers:
    - name: download-assets
      image: busybox
      command: ["wget", "-O", "/data/config.json",
                "https://example.com/config.json"]
      volumeMounts:
        - name: data
          mountPath: /data

The app container mounts the same volume and reads the data prepared by the init container. Both containers reference the same volume name.

  containers:
    - name: app
      image: my-app:v1
      volumeMounts:
        - name: data
          mountPath: /app/data

emptyDir creates a temporary directory shared between containers. It persists across init and app containers but is deleted when the Pod terminates.

  volumes:
    - name: data
      emptyDir: {}
pod-wait-deps.yaml

Wait for dependencies before starting the main app. Avoids race conditions during rollouts. Better than retry logic in your application code.

initContainers:
  - name: wait-for-db
    image: busybox:1.28
    command:
      - sh
      - -c
      - |
        until nc -z postgres 5432; do
          echo "Waiting for postgres..."
          sleep 2
        done
  - name: wait-for-redis
    image: busybox:1.28
    command:
      - sh
      - -c
      - until nc -z redis 6379; do sleep 2; done
pod-init-resources.yaml

Init containers can have different resource limits than app containers. Useful when init needs more memory (migrations) but app needs less. Pod resource limits are the max of init and app containers.

spec:
  initContainers:
    - name: heavy-migration
      image: my-app:v1
      command: ["./migrate.sh"]
      resources:
        requests:
          memory: "512Mi"
          cpu: "500m"
        limits:
          memory: "1Gi"
  containers:
    - name: app
      image: my-app:v1
      resources:
        requests:
          memory: "128Mi"
terminal

Debug init containers when Pods stay in Init state. Pod stuck in Init:0/2 means 0 of 2 init containers completed. Check logs for each init container by name.

$ kubectl get pods
NAME     READY   STATUS     RESTARTS   AGE
my-app   0/1     Init:1/3   0          30s

$ kubectl logs my-app -c migrations
Running database migrations...
Migration failed: connection refused

$ kubectl describe pod my-app | grep -A20 "Init Containers"
Init Containers:
  migrations:
    State:      Terminated
    Reason:     Error
    Exit Code:  1

$ kubectl describe pod my-app | grep -A10 Events
Events:
  Type     Reason   Message
  Warning  Failed   Error: Init container failed
pod-init-timeout.yaml

Init containers don’t support probes (liveness, readiness). They run to completion and exit. Use proper exit codes and timeouts in your init scripts to prevent hanging.

initContainers:
  - name: wait-for-service
    image: busybox:1.28
    command:
      - sh
      - -c
      - |
        timeout 60 sh -c 'until nc -z api 80; do sleep 1; done'
        if [ $? -ne 0 ]; then
          echo "Timeout waiting for api"
          exit 1
        fi

Index | Use arrow keys to navigate