Skip to main content
Course/Primitives/Init Containers & Lifecycle Hooks
4/845 min
Primitives

Init Containers & Lifecycle Hooks

Run setup tasks before your app starts and handle graceful shutdown with preStop hooks.

beginner45 minRead β†’ Lab β†’ Quiz β†’ Practice
🧠 Brain Warm-Up
🧠 Brain Warm-Up: Your application needs a database schema migration to run before the API server accepts traffic. Both are in the same Pod. How do you guarantee the migration completes successfully before the API container starts? Think about startup ordering within a Pod.

Init Containers

An init container runs to completion before any of the main containers start. This gives you sequenced, guaranteed setup:

Pod starts
  ↓
init-container-1 runs β†’ exits 0
  ↓
init-container-2 runs β†’ exits 0
  ↓
main containers start (in parallel)

Key properties:

  • Run sequentially β€” each must complete before the next starts
  • If an init container fails, kubelet restarts it (respecting the Pod's restartPolicy)
  • Main containers cannot start until all init containers succeed
  • Can use a different image than the main container (e.g., a kubectl image for setup, vault for secret fetching)

Common Patterns

Pattern 1: Wait for a dependency
  initContainers:
  - name: wait-for-db
    image: busybox
    command: ['sh', '-c', 'until nc -z mysql-svc 3306; do sleep 2; done']

Pattern 2: Fetch secrets from Vault
  initContainers:
  - name: vault-init
    image: vault:latest
    command: ['vault', 'agent', 'render', '-config=/vault-agent.hcl']
    # Writes secrets to a shared emptyDir volume

Pattern 3: Run database migrations
  initContainers:
  - name: migrate
    image: myapp:latest
    command: ['./manage.py', 'migrate', '--no-input']

Lifecycle Hooks

Kubernetes provides two hooks that run code at specific moments in a container's lifecycle:

postStart

Fires immediately after a container is created β€” concurrently with the container's main process (ENTRYPOINT). The container does not reach Running state until postStart completes.

⚠️ Warning: postStart is NOT guaranteed to run before ENTRYPOINT. For strict ordering, use init containers instead.

preStop

Fires before SIGTERM is sent. Kubernetes waits for preStop to finish, then sends SIGTERM. Critical for graceful shutdown β€” drain in-flight requests, deregister from service discovery, flush write buffers.

lifecycle:
  preStop:
    exec:
      command: ['/bin/sh', '-c', 'nginx -s quit; sleep 5']

terminationGracePeriodSeconds

When a Pod is deleted:

kubectl delete pod nginx
         ↓
preStop hook runs         Pod removed from Service Endpoints
(simultaneously)          (new traffic stops arriving)
         ↓
SIGTERM sent to process
         ↓  (app drains in-flight requests)
[terminationGracePeriodSeconds countdown β€” default 30s]
         ↓  (if still alive after grace period)
SIGKILL

For apps that take longer than 30 seconds to drain, set terminationGracePeriodSeconds to match. Combine with a preStop sleep to give load balancers time to deregister the Pod:

spec:
  terminationGracePeriodSeconds: 60
  containers:
  - lifecycle:
      preStop:
        exec:
          command: ['sleep', '15']  # Wait for LB health check deregistration