Native Sidecar Containers: The Init Container Trick That Fixed Lifecycle Bugs
Kubernetes native sidecars solve the old problems of pods that never finish and proxies that die too early. Learn how restartPolicy Always on init containers changes the game.
- #kubernetes
- #sidecars
- #init-containers
- #pods
- #lifecycle
For years, the sidecar pattern in Kubernetes was held together with workarounds. We’d run a proxy or a log shipper as a regular container alongside the app, and then fight two predictable bugs: a Job pod that never completed because the sidecar kept running after the main work finished, and a main container that started before its proxy sidecar was ready, failing every request for the first few seconds. I wrote shell-script hacks for both of these more times than I’d like to admit.
Native sidecar containers — stable since Kubernetes 1.29 — fix this properly. They’re implemented as init containers with a restartPolicy: Always, which sounds like a contradiction until you see what it does to the pod lifecycle. This guide explains the old problems, how native sidecars solve them, and where an AI assistant helps.
The two bugs native sidecars fix
The classic sidecar-as-regular-container approach has two structural flaws:
Startup ordering. Regular containers in a pod start in parallel. If your app container comes up before the Istio or Envoy proxy sidecar is ready, every outbound request fails until the proxy catches up. People papered over this with holdApplicationUntilProxyStarts flags and init-container readiness loops.
Shutdown / completion. In a Job, the pod is “complete” only when all containers exit. A log-shipping sidecar that runs forever means the Job pod never reaches Completed — it just hangs with the main container done and the sidecar idling. The workarounds involved the main container writing a sentinel file that the sidecar watched for so it could exit 0.
Native sidecars eliminate both by changing where the sidecar sits in the lifecycle.
How a native sidecar is declared
The mechanism is delightfully simple: you put the sidecar in initContainers and give it restartPolicy: Always.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
template:
spec:
initContainers:
- name: proxy
image: envoyproxy/envoy:v1.31-latest
restartPolicy: Always # <- this makes it a sidecar
ports:
- containerPort: 15001
startupProbe:
httpGet:
path: /ready
port: 15021
containers:
- name: app
image: registry.internal/web:2.0.1
ports:
- containerPort: 8080
That one field, restartPolicy: Always on an init container, changes everything. Here’s the new behavior:
- The sidecar starts before the main containers and the pod waits for its startup probe — so the app never comes up before its proxy. Ordering bug solved.
- The sidecar keeps running alongside the main containers (unlike a normal init container, which must exit before the next one starts).
- The sidecar does not block pod completion — when the main containers finish, the kubelet terminates the sidecars and the pod completes. The hanging-Job bug solved.
Ordering multiple sidecars
Because native sidecars are init containers, they start in the order you list them, each waiting for the previous one’s startup probe. That gives you deterministic ordering that regular containers never had:
initContainers:
- name: vault-agent # 1. fetch secrets first
image: hashicorp/vault:1.17
restartPolicy: Always
- name: proxy # 2. then the network proxy
image: envoyproxy/envoy:v1.31-latest
restartPolicy: Always
containers:
- name: app # 3. finally the app, with secrets + proxy ready
image: registry.internal/web:2.0.1
You can still mix in real init containers (no restartPolicy) for one-shot setup like running database migrations. Those run to completion in order, interleaved with the sidecars, and the app waits for all of it.
Pro Tip: Native sidecars terminate in reverse order during pod shutdown, and after the main containers. This is exactly what you want for a proxy — it stays alive to drain the app’s final connections before it dies. But it means a sidecar with a slow shutdown can stretch your pod’s total termination time. Keep sidecar terminationGracePeriodSeconds honest, and test a rolling update to confirm pods don’t hang in Terminating.
Migrating an existing sidecar
If you have a Deployment using the old pattern, the migration is mechanical: move the sidecar definition from spec.template.spec.containers to spec.template.spec.initContainers and add restartPolicy: Always. Then delete whatever workaround you had — the sentinel files, the holdApplicationUntilProxyStarts, the readiness-loop init container. Test with a Job specifically, because that’s where the completion behavior is most visibly different:
kubectl apply -f job-with-native-sidecar.yaml
kubectl get pods -w # should reach Completed, not hang
kubectl get job <name> -o jsonpath='{.status.succeeded}'
Check your cluster version first — native sidecars need the SidecarContainers feature, which is on by default from 1.29 onward. On older clusters the field is silently ignored and you get the old parallel behavior, which is a confusing way to learn your nodes are out of date.
Where AI helps with the migration
Converting sidecars across a fleet of manifests is repetitive pattern-matching, which is exactly what an AI assistant does well. I’ll paste a Deployment using the old approach and ask the model to produce the native-sidecar version, identify which workaround code is now redundant, and flag whether the sidecar needs a startup probe for ordering to work. Across twenty manifests, that saves an afternoon of careful YAML surgery.
I treat the assistant as a fast junior engineer: it converts manifests and explains the lifecycle implications, but it doesn’t apply anything. These changes alter pod startup and shutdown ordering — get them wrong and a Job hangs or a proxy dies mid-request — so every edit is human-in-the-loop. I read each converted manifest, render it, and apply it myself. The model never receives a kubeconfig or production credentials; it works on YAML text only and runs nothing against a live cluster. For a structured review of the converted manifests there’s the code review dashboard, and reusable starting prompts in the prompt library and prompt packs.
A conversion prompt that works well:
Convert this Deployment from the legacy sidecar pattern (sidecar as a
regular container) to native sidecars (init container with
restartPolicy: Always). Add a startup probe if ordering requires it,
list any workaround config that is now redundant, and note the minimum
Kubernetes version required. Output YAML and notes only — no kubectl.
A few things to watch
Resource accounting changed subtly: native sidecar resource requests now count toward the pod’s effective request for scheduling, which is more correct than the old behavior but may make tightly-packed nodes reject pods they used to accept. Probe your sidecars with startupProbe rather than readinessProbe if their purpose is purely ordering — a readiness probe on a sidecar can flap the whole pod’s readiness in ways you didn’t intend. And remember that a crashing native sidecar restarts (because restartPolicy: Always) rather than failing the pod, which is usually what you want but means a broken sidecar shows up as restart churn, not a clean failure.
Wrapping up
Native sidecars take a pattern we used to hold together with shell scripts and make it a first-class lifecycle feature. Declare the sidecar as an init container with restartPolicy: Always, order multiple sidecars deterministically, and let Job pods finally complete the way they should. Use an AI assistant to do the mechanical migration and explain the lifecycle changes while you keep your hands on every apply and your credentials off the model’s plate. The first hung Job you don’t get paged for makes the switch worth it. For more pod-lifecycle patterns, browse the Kubernetes & Helm guides.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.