Topic 1.6
Init Containers & Sidecars: Helpers Inside a Pod
In one line
Init containers run one after another before the app container starts, and each must succeed: waiting for a dependency, running a setup step, fetching configuration. Sidecar containers run alongside the app for its whole life: log shippers, proxies, config reloaders. Since Kubernetes 1.29 (stable in 1.33), a native sidecar is an init container with restartPolicy: Always, which starts before the app and stops after it.
Think of it like this
Opening a restaurant. Before the doors open, staff check the gas, set the tables, and stock the fridge (init containers, one after another). During service, a host greets guests next to the kitchen (sidecar). The kitchen (app) only starts once setup is done.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Init container
- A container that runs to completion before the app containers start.
- Sidecar container
- A helper container running alongside the app for the pod's lifetime.
- Native sidecar
- An init container with
restartPolicy: Always, started first and stopped last (Kubernetes 1.29+). - Shared volume
- A volume mounted by several containers in a pod so they can exchange files.
- Startup ordering
- The order in which a pod's containers start and stop.
Step by step
01Wait for the database, then start
In the dev environment, the web pod sometimes starts before Postgres is ready and crashes a few times. An init container waits until Postgres accepts connections. (In production, the app should also retry connections itself.)
spec:
initContainers:
- name: wait-for-db
image: postgres:16.4
command: ["sh", "-c", "until pg_isready -h postgres -p 5432; do echo waiting for db; sleep 2; done"]
resources: { requests: { cpu: 10m, memory: 16Mi } }
containers:
- name: web
image: ghcr.io/tiffin-team/web:2.2.002A native sidecar for log shipping
A legacy reporting app writes logs to a file instead of stdout. A native sidecar tails the shared file and ships it. Because it's declared with restartPolicy: Always in initContainers, it's running before the app starts writing and keeps running until the app has stopped.
spec:
initContainers:
- name: log-shipper
image: cr.fluentbit.io/fluent/fluent-bit:3.2
restartPolicy: Always # native sidecar
args: ["-i", "tail", "-p", "path=/logs/app.log", "-o", "stdout"]
volumeMounts: [{ name: logs, mountPath: /logs }]
containers:
- name: reports
image: ghcr.io/tiffin-team/reports:3.0.1
volumeMounts: [{ name: logs, mountPath: /var/log/reports }]
volumes:
- name: logs
emptyDir: {}03Why native sidecars matter for Jobs
With an old-style sidecar in containers, a Job's pod never completes: the main container finishes, but the sidecar keeps running, so the Job waits forever. A native sidecar is stopped automatically once the main container exits.
Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
An init container that never finishes
The wait-for-db init container points at postgres.tiffin-db (a namespace that doesn't exist in staging).
Myth vs fact
Myth
Init containers make the app wait for dependencies forever after.
Fact
They only gate startup. If the database goes away later, the app must handle reconnecting on its own.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Service meshes inject their proxy as a sidecar automatically (Topic 8.3). With native sidecars, the proxy is ready before the app's first outgoing call and outlives the app's final requests, fixing classic startup and shutdown races.
Remember this
- 1
Init containers (
spec.initContainers) run in order, each to completion. If one fails, the pod restarts it (per restartPolicy) and the app never starts until all succeed. - 2
Uses: waiting for a database to accept connections, running migrations in simple setups, copying files into a shared volume, setting kernel parameters (with privileges), fetching secrets.
- 3
Sidecars run alongside the app container: a log forwarder reading a shared volume, a proxy (service mesh), a certificate reloader, or a cloud SQL proxy.
- 4
Native sidecars: declared in
initContainerswithrestartPolicy: Always. They start before the main containers (and can have probes), keep running, restart if they crash, and are stopped after the main container exits, which matters for Jobs and graceful shutdown. - 5
Old-style sidecars (just another container in
containers) have no start order and keep Jobs from finishing, because the Job waits for all containers to exit. - 6
Init containers count towards scheduling with the highest of their requests, and sidecars add their requests to the app's, so give them small requests.
Explain it without notes
How do init containers and sidecars differ?
Why did old-style sidecars break Jobs?
Practice
Add an init container that writes a file into an emptyDir, and have the app container read it.
Convert an old-style sidecar into a native sidecar.
Trade-offs
- ↔
Init containers keep setup logic out of the app image, but add startup time and can hide dependency problems. Sidecars add capabilities without changing the app, at the cost of extra resources per pod.
Done when you can
I can use init containers for setup steps with timeouts.
I know native sidecars and why they help Jobs.
I keep helper containers' resource requests small.