DevOps Dictionary

Kubernetes Init Container

Kubernetes Init Container

Kubernetes Init Container is a container that runs before the main application containers in a pod. It performs setup work that must finish successfully before the application starts, such as preparing configuration, waiting for a dependency, running a database migration, or copying files into a shared volume.

Init containers are part of the pod spec. Kubernetes runs them in order, one at a time. When every init container exits successfully, Kubernetes starts the app containers.

What an init container does

An init container handles startup tasks that do not belong inside the long-running application process. Common tasks include:

  • Waiting until a database, message broker, or internal API is reachable.
  • Generating configuration files before the app starts.
  • Running schema migrations or one-time setup scripts.
  • Downloading assets, certificates, or bootstrap data.
  • Copying files into an emptyDir volume shared with the main container.
  • Checking that required Kubernetes Secrets or ConfigMaps exist.

How it works

Init containers are defined under spec.initContainers in a Pod manifest. Kubernetes starts the first init container and waits for it to complete with exit code 0. Then it starts the next one. If any init container fails, Kubernetes retries it based on the pod restart policy.

The app containers under spec.containers do not start until all init containers complete successfully.

A simplified pod flow looks like this:

  1. Kubernetes schedules the pod onto a node.
  2. The kubelet pulls the init container image.
  3. The first init container runs and exits successfully.
  4. Kubernetes runs the next init container, if one exists.
  5. After all init containers finish, Kubernetes starts the app containers.

Simple example

Suppose your app needs a database to be reachable before it starts. You can use an init container to wait for the database service:

apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  initContainers:
    - name: wait-for-db
      image: busybox:1.36
      command:
        - sh
        - -c
        - until nc -z postgres 5432; do echo waiting for db; sleep 2; done
  containers:
    - name: app
      image: my-app:1.0.0
      ports:
        - containerPort: 8080

In this example, the app container starts only after the wait-for-db init container can connect to postgres:5432.

Common use cases

  • Dependency checks: Wait for PostgreSQL, Redis, Kafka, or another service before starting the app.
  • Database migrations: Run a migration script before the application starts serving traffic.
  • Config preparation: Build a config file from environment variables, Secrets, or mounted files.
  • Security setup: Set file permissions on a mounted volume before the main container runs as a non-root user.
  • Data seeding: Load initial data for local development, test environments, or short-lived review apps.
  • Infrastructure workflows: Validate that cloud resources exist before an app starts, especially in setups using tools like Crossplane. For example, teams can define cloud resources through Kubernetes using Crossplane on Kubernetes.

Init container versus app container

An init container is meant to run and exit. An app container is meant to keep running.

  • Init container: Runs before app containers, performs setup, then exits.
  • App container: Runs the main workload, such as an API server, worker, or web app.

Because init containers must complete, they do not use readiness, liveness, or startup probes in the same way long-running app containers do.

Init container versus sidecar container

A sidecar container runs alongside the main application container. It usually supports the app while it runs, such as a log shipper, proxy, or local agent.

An init container runs before the app and then stops. Use an init container for setup work. Use a sidecar for ongoing work.

Benefits

  • Cleaner application images: You can keep setup tools out of the main app image. For example, the app image does not need curl, nc, or migration CLIs if only the init container uses them.
  • Clear startup ordering: Kubernetes gives you a defined sequence before the app starts.
  • Separate permissions: An init container can run with different security settings from the app container.
  • Reusable setup logic: You can use the same init container image across multiple services that need the same startup step.
  • Better failure behavior: If setup fails, the pod does not start serving traffic in a broken state.

Tradeoffs and limitations

  • Slow startup: Long init tasks increase pod startup time, which can affect deploys, autoscaling, and recovery.
  • Retry loops: A failing init container can leave a pod stuck in Init:CrashLoopBackOff.
  • Resource impact: Kubernetes considers init container resource requests during scheduling. A large init container can make the pod harder to schedule, even if the main app is small.
  • Not for ongoing tasks: Do not use init containers for log forwarding, metrics agents, proxies, or background daemons. Use a sidecar or separate workload instead.
  • Migration risk: Running database migrations in init containers can cause problems when many replicas start at once. Use locking, a dedicated Job, or a release pipeline step when needed.

Operational considerations

Init containers affect deployment behavior. If you deploy Kubernetes resources with tools such as Terraform, make sure your manifests define init containers, volumes, Secrets, and ConfigMaps in the right order. This is especially relevant when you deploy Kubernetes resources using Terraform.

For workloads on managed Kubernetes services like Amazon EKS, init containers work the same way as in standard Kubernetes. They are useful for platform workloads such as Airflow, where setup tasks may need to prepare connections, metadata, or config before schedulers and workers start. A similar pattern appears when teams deploy Apache Airflow on AWS EKS.

During cluster upgrades, check init container images and commands as carefully as app containers. Old images, deprecated APIs, or missing permissions can break pod startup after an upgrade. This should be part of your Kubernetes upgrade checklist.

Best practices

  • Keep init containers small and focused on one task.
  • Set CPU and memory requests and limits explicitly.
  • Use short timeouts in scripts so failures are visible.
  • Log useful status messages, such as which dependency is unavailable.
  • Avoid sleeping for a fixed time when you can check the actual dependency state.
  • Use Kubernetes Jobs for complex one-time workflows, especially database migrations that should run once per release.
  • Use separate ServiceAccounts and permissions when the init container needs access the app should not have.

Example in practice

A startup runs a Node.js API on Kubernetes. The app needs a generated config file that includes a cluster-specific internal URL and a mounted certificate. Instead of adding shell tools to the Node.js image, the team adds an init container based on a small Linux image. The init container reads the mounted Secret, writes /config/app.yaml into an emptyDir volume, then exits. The Node.js container mounts the same volume and starts with the generated config.

This keeps the runtime image smaller and makes the setup step visible in pod status. If the Secret is missing, the pod fails during initialization instead of starting the API with invalid config.

Key takeaway

A Kubernetes init container is the right tool for ordered, one-time setup inside a pod. Use it when the main application should not start until a dependency, file, permission, or configuration step is complete.

A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
Y
X
Z