Chapter 34 of 47 · Pod patterns

Init containers and sidecars

A Pod can hold more than your app. Init containers do setup work before your app starts, like a stage crew building the set before the show. Sidecars run next to your app the whole time, like a sound engineer who stays for the full concert. Both let you keep your app small and focused.

The picture

Order of containers in a PodInit container 1 runs and finishes, then init container 2 runs and finishes. Only then do the app container and the sidecar start, and they run side by side. POD posts init 1wait for event bus init 2run DB migration posts (your app)runs the whole time log-shipper (sidecar)runs the whole time, next to the app one after another, each must finish time →
Init containers run first, one at a time. Then the app and its sidecars run together.

Init containers

You met one in chapter 14: a tiny container that waited for event-bus-srv before posts started. The rules are simple:

Click the parts of this Pod:

apiVersion: v1
kind: Pod
metadata:
  name: posts
spec:
  initContainers:
    - name: wait-for-bus
      image: busybox:1.36
      command: ["sh", "-c", "until nslookup event-bus-srv; do sleep 2; done"]
    - name: migrate
      image: micro-posts-migrate:1.0
      command: ["npm", "run", "migrate"]
  containers:
    - name: posts
      image: micro-posts:1.0
      volumeMounts:
        - name: shared
          mountPath: /app/data
  volumes:
    - name: shared
      emptyDir: {}

initContainers

A list, next to containers. Kubernetes runs wait-for-bus first. Only when it exits with 0 does migrate start. Only when that also succeeds does posts start.

$ kubectl get pods
NAME    READY   STATUS     RESTARTS   AGE
posts   0/1     Init:0/2   0          5s     ← first init container running
posts   0/1     Init:1/2   0          12s    ← first done, second running
posts   1/1     Running    0          20s    ← both done, app started

$ kubectl logs posts -c migrate          # logs of one init container

Four classic init jobs

- name: wait-for-db
  image: busybox:1.36
  command: ["sh", "-c", "until nc -z db 5432; do echo waiting for db; sleep 2; done"]

Do not start the app until the database answers. Without this, the app crashes a few times at startup and gets restarted, which works but looks messy and slows everything down.

When an init container fails

If an init container exits with an error, the Pod does not start. Kubernetes tries again, waiting longer each time, and the status tells you exactly where it is stuck:

  1. posts   0/1   Init:0/2           0   3s

    wait-for-bus is running.

  2. posts   0/1   Init:1/2           0   10s

    The event bus answered. migrate starts.

  3. posts   0/1   Init:Error         1   14s
    posts   0/1   Init:CrashLoopBackOff   2   40s

    The migration fails. Kubernetes restarts that init container (with the default restartPolicy: Always), with longer and longer pauses. The app container never starts.

  4. $ kubectl logs posts -c migrate
    Error: column "author_id" already exists

    Read the init container's own logs with -c. Fix the problem; the next retry succeeds and the app starts.

Resources: the Pod reserves the bigger of (largest init container request) and (sum of app container requests), because init containers never run at the same time as the app. A heavy migration step does not make the running Pod more expensive.

Sidecars

A sidecar is a second container that keeps running next to your app and adds a feature, without changing your app's code. Like a sidecar on a motorbike: attached, travels along, separate seat.

A sidecar shares the Pod with the app POD postswrites log files log-shippersends them away shared volume /logs logging system

Log shipper

Reads the app's log files from a shared volume and sends them to your logging system.

Proxy

Handles HTTPS or authentication in front of the app, which keeps speaking plain HTTP on localhost.

git-sync

Keeps a folder in sync with a Git repo, e.g. the static files a web server shows.

Config reloader

Watches a ConfigMap file and tells the app to reload when it changes (chapter 18).

Each helper can be written in any language, updated separately, and reused next to many apps. Adapters and ambassadors (chapter 35) are two special kinds of sidecar.

Native sidecars

The old way to add a sidecar was simply a second entry under containers. That has two annoying problems: Kubernetes does not know which one is "the app", so the sidecar may start after the app (logs from the first seconds are lost), and in a Job (chapter 36) the Pod never finishes because the sidecar never exits.

The fix, stable since Kubernetes 1.33: put the sidecar under initContainers with restartPolicy: Always.

spec:
  initContainers:
    - name: log-shipper
      image: fluent-bit:3.0
      restartPolicy: Always        # ← this makes it a sidecar, not a normal init step
      volumeMounts: [{ name: logs, mountPath: /logs }]
  containers:
    - name: posts
      image: micro-posts:1.0
      volumeMounts: [{ name: logs, mountPath: /var/log/app }]
  volumes:
    - name: logs
      emptyDir: {}
start:  log-shipper ✓ (running) → posts starts
stop:   posts stops first → log-shipper stops last
Job:    task finishes → sidecar is stopped → Job Complete ✓

Starts first, stops last, and never keeps a Job alive. It also restarts by itself if it crashes.

Init, sidecar or postStart?

Init containerNative sidecarpostStart hook
RunsBefore the app, then exitsBefore and during the appRight after the app starts, in parallel
Own imageYesYesNo, runs inside the app container
Order guaranteedYesStarts before the appNo
Good forSetup: wait, migrate, downloadHelpers: logs, proxy, syncTiny extras (chapter 14)

Recap

Quick check

A Pod shows Init:1/3 for ten minutes. What does that tell you?
Show answer

The first of three init containers finished, and the second is still running or retrying. Run kubectl describe pod and kubectl logs <pod> -c <second-init-name> to see why.

A Job with a log-shipping sidecar runs its task in two minutes but never shows Complete. Fix?
Show answer

The sidecar is a normal container and never exits. Move it under initContainers with restartPolicy: Always, making it a native sidecar that is stopped when the task ends.

Init containers and sidecars: common questions

What is an init container in Kubernetes?

A container that runs to completion before the app containers start. Several init containers run one after another, and each must succeed first.

What is a native sidecar container?

A container listed under initContainers with restartPolicy: Always. It starts before the app, keeps running beside it, and stops after it, so Jobs can still complete.