🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

What is an Init Container in Kubernetes? Explained Simply

Init containers run before your main app container starts. Here's what they are, when to use them, and real examples for database migrations, config setup, and more.

DevOpsBoys4 min read
Share:Tweet

An init container is a special container that runs before your main application container starts. Once it completes successfully, Kubernetes starts the main container. If it fails, the pod restarts.

Think of it as a setup step that must pass before your app is allowed to run.

Why Do They Exist?

Some tasks need to happen before your app starts:

  • Database migrations — run schema changes before the app tries to use the DB
  • Wait for dependencies — don't start until the database/API is ready
  • Download config files — fetch secrets or config from an external source
  • Set permissions — change file ownership on a shared volume before the app reads it

You could do all of this in your main container's entrypoint script, but that mixes concerns and makes your container image messy. Init containers keep this separate and clean.

Basic Example

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  template:
    spec:
      initContainers:
        - name: wait-for-db
          image: busybox:1.36
          command: ['sh', '-c', 'until nc -z postgres-service 5432; do echo waiting for postgres; sleep 2; done']
      
      containers:
        - name: my-app
          image: my-app:v1.0.0
          ports:
            - containerPort: 8080

The wait-for-db init container keeps trying until it can connect to port 5432 on postgres-service. Only then does my-app start. No more "connection refused" errors on startup.

Init Container vs Sidecar Container

Init ContainerSidecar Container
When it runsBefore main containerAlongside main container
LifecycleRuns once, then exitsRuns for pod's lifetime
Must succeedYes (pod restarts if it fails)No (independent lifecycle)
Typical useSetup tasksLogging, proxies, monitoring agents

Real Example: Database Migration with Flyway

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-api
spec:
  template:
    spec:
      initContainers:
        - name: run-migrations
          image: flyway/flyway:10
          args:
            - migrate
          env:
            - name: FLYWAY_URL
              value: "jdbc:postgresql://postgres-service:5432/mydb"
            - name: FLYWAY_USER
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: username
            - name: FLYWAY_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
          volumeMounts:
            - name: migrations
              mountPath: /flyway/sql
      
      containers:
        - name: api
          image: backend-api:v2.0.0
          # starts only after migrations succeed
      
      volumes:
        - name: migrations
          configMap:
            name: db-migrations

The Flyway init container runs all pending SQL migrations. If a migration fails, the pod restarts and tries again. Your application never starts with an outdated schema.

Real Example: Wait for a Service to Be Ready

yaml
initContainers:
  - name: wait-for-redis
    image: redis:7-alpine
    command:
      - sh
      - -c
      - |
        until redis-cli -h redis-service -p 6379 ping | grep -q PONG; do
          echo "Waiting for Redis..."
          sleep 2
        done
        echo "Redis is ready!"

Real Example: Copy Static Assets to Shared Volume

yaml
spec:
  initContainers:
    - name: copy-assets
      image: my-frontend:v1.0.0
      command: ['cp', '-r', '/app/dist/.', '/shared-assets/']
      volumeMounts:
        - name: shared-assets
          mountPath: /shared-assets
  
  containers:
    - name: nginx
      image: nginx:alpine
      volumeMounts:
        - name: shared-assets
          mountPath: /usr/share/nginx/html
  
  volumes:
    - name: shared-assets
      emptyDir: {}

The init container copies built frontend files to an emptyDir volume. The nginx container then serves them. This way you don't need nginx + node in the same image.

Multiple Init Containers

You can have multiple init containers. They run in order — each must complete before the next starts:

yaml
initContainers:
  - name: step-1-wait-for-db
    image: busybox
    command: ['sh', '-c', 'until nc -z db 5432; do sleep 2; done']
  
  - name: step-2-run-migrations
    image: flyway/flyway:10
    args: [migrate]
    env:
      - name: FLYWAY_URL
        value: "jdbc:postgresql://db:5432/myapp"
  
  - name: step-3-seed-cache
    image: redis:7-alpine
    command: ['sh', '-c', 'redis-cli -h redis SET warmup_done 1']

They run in this exact order: wait → migrate → seed. If step 2 fails, step 3 never runs.

Checking Init Container Status

bash
# See pod status including init containers
kubectl get pod my-app-pod -o wide
 
# Detailed view
kubectl describe pod my-app-pod

You'll see output like:

Init Containers:
  wait-for-db:
    State: Terminated
      Reason: Completed
      Exit Code: 0
  run-migrations:
    State: Running

Get logs from an init container:

bash
kubectl logs my-app-pod -c wait-for-db
kubectl logs my-app-pod -c run-migrations

If the init container fails, the pod shows Init:CrashLoopBackOff or Init:Error. Check logs to see why.

Key Rules

  1. Init containers must complete with exit code 0 (success) before the main container starts
  2. If an init container fails, Kubernetes restarts the entire pod (respecting restartPolicy)
  3. Init containers don't have readiness probes — they must run to completion
  4. Init containers share volumes and network namespace with the main container
  5. Multiple init containers run sequentially, not in parallel

Init containers are one of the cleanest patterns in Kubernetes for handling startup dependencies. Use them instead of putting sleep loops in your application entrypoint.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments