Kubernetes Native Sidecar Containers: A Practical Guide

Photo by Pix Tresa on Unsplash
They are init containers declared with restartPolicy set to Always. Unlike a normal init container that runs to completion, a sidecar starts before the main app, keeps running for the whole Pod lifetime, and is shut down after the app exits. It is the built-in replacement for the old ad-hoc pattern of adding a second app container.
Add restartPolicy set to Always to an entry in the Pod's initContainers list. That single field is the switch — the kubelet then marks it started, moves on to the next container, and keeps the sidecar running. Always is currently the only valid value for an init container restartPolicy.
Native sidecar containers reached stable (GA) in Kubernetes v1.33, released on 23 April 2025. They first appeared as alpha in v1.28 behind the SidecarContainers feature gate in August 2023, then became beta and enabled by default in v1.29. From v1.29 onward most clusters need no feature-gate changes.
A Job completes when its containers finish, but a logging or proxy sidecar runs indefinitely by design. In the old pattern that sidecar was a regular app container, so the main task ended while the sidecar kept running and the Job never completed. Native sidecars fix this because Pod termination now depends only on the main containers.
Yes. Unlike regular init containers, native sidecars support startup, readiness, and liveness probes. A sidecar's readinessProbe feeds into the Pod's ready state, so an unhealthy mesh proxy keeps the Pod out of a Service's endpoints until it recovers.

Photo by Pix Tresa on Unsplash
Key Takeaway
Kubernetes native sidecar containers are init containers declared with restartPolicy set to Always. The kubelet starts them before the main app, keeps them running for the whole Pod lifetime, and shuts them down after the app exits. This fixes broken startup ordering and Jobs that never complete. The feature is stable since Kubernetes v1.33.
For years the sidecar was a pattern, not a feature. You added a second container to a Pod — a log shipper, a service-mesh proxy, a metrics exporter — and hoped it behaved. Kubernetes had no concept of a sidecar, so it treated that second container exactly like your main application. That equal treatment is where the pain came from, and it is what the native sidecar feature finally removed.
I have hit both classic failure modes in production: a proxy that was not ready when the app made its first outbound call, and a batch Job that ran forever because its logging sidecar never stopped. Native sidecar containers solve both by giving auxiliary containers a proper, ordered lifecycle instead of pretending they are just more app containers.
The ad-hoc pattern had no ordering and no lifecycle awareness. All containers in a Pod start roughly in parallel and are treated as peers, which produced three recurring problems.
The insight behind KEP-753 is that a sidecar is really a special init container. Init containers already have a well-defined, sequential startup order, so building sidecars on top of them gives ordering for free. You add one field — restartPolicy set to Always — to an entry in the Pod's initContainers list, and that container becomes a sidecar.
That single field changes three behaviours. The kubelet marks the init container as started and moves on to the next one instead of waiting for it to exit, so your app container starts only after the sidecar is up. The sidecar then keeps running for the whole Pod lifetime and is restarted on its own if it crashes. On termination the kubelet stops the main containers first, then shuts sidecars down in the reverse of their declared order.
Only Always is valid as an init container restartPolicy today. That one value is the switch that converts an ordinary init container into a long-lived sidecar — there is no separate sidecar kind or API object to learn.
Here is a Deployment with a log-shipper sidecar. It lives under initContainers, but restartPolicy Always keeps it running alongside the app. A startupProbe lets the kubelet hold the main container back until the shipper is genuinely ready, not just created.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: registry.example.com/web:1.6.0
ports:
- containerPort: 8080
volumeMounts:
- name: logs
mountPath: /var/log/app
initContainers:
# restartPolicy: Always turns this init container into a
# native sidecar: it starts before "web", stays running for
# the whole Pod lifetime, and shuts down after "web" exits.
- name: log-shipper
image: registry.example.com/log-shipper:2.1.0
restartPolicy: Always
startupProbe:
httpGet:
path: /healthz
port: 3000
volumeMounts:
- name: logs
mountPath: /var/log/app
volumes:
- name: logs
emptyDir: {}The same block works in a Job. Because the sidecar is an init container with restartPolicy Always, Pod termination now depends only on the main containers — when the batch task finishes, the Job completes and the sidecar is shut down automatically. No more Jobs stuck in a running state waiting on a sidecar that never exits.
Native sidecars support startup, readiness, and liveness probes, which regular init containers do not. A sidecar's readinessProbe feeds into the Pod's ready state, so a mesh proxy that is not healthy keeps the Pod out of a Service's endpoints. Resource requests are summed with the app containers rather than compared against them, and OOM score handling is aligned with the main containers so a sidecar is not killed first under memory pressure.
During shutdown a sidecar can receive SIGTERM and then SIGKILL if the main containers consume the entire grace period. A non-zero exit code from a sidecar on Pod termination is expected and should generally be ignored by external tooling — do not alert on it.
The difference is not cosmetic. The native approach moves ordering, lifecycle, and shutdown from application-level workarounds into the kubelet, where they belong. This table sums up what actually changes.
| Concern | Old ad-hoc sidecar | Native sidecar container |
|---|---|---|
| Where it is declared | A second entry in containers | An initContainer with restartPolicy Always |
| Startup ordering | None — starts in parallel with the app | Guaranteed to start before the app container |
| Job completion | Job hangs — sidecar never exits | Job completes once the main task finishes |
| Shutdown order | Undefined — proxy may die first | App stops first, sidecars in reverse order |
| Health probes | Only via full app container plumbing | Startup, readiness, and liveness supported |
The feature has a clear graduation path. It landed as alpha in Kubernetes v1.28 in August 2023 behind the SidecarContainers feature gate, went to beta and became enabled by default in v1.29, and graduated to stable in v1.33, released on 23 April 2025. From v1.29 onward you can use it without touching feature gates on most clusters.
My advice: if your cluster is on v1.29 or newer, move mesh proxies, log shippers, and metrics agents to native sidecars now — you get correct ordering and clean shutdown with a one-line change. For batch and AI/ML Jobs the win is even larger, because it removes the Job-never-completes class of bug entirely. Start with one non-critical workload, confirm the Pod becomes ready only after the sidecar, and roll it out from there.