Kubernetes explainer

Sidecar vs Init Containers: Native Sidecar Setup

An init container runs to completion before the app starts. A sidecar runs next to the app for the life of the pod. For years Kubernetes had no first-class way to express the second kind, so teams put helpers in the containers list and hoped for the best. Native sidecars, on by default since Kubernetes 1.29, add a first-class form, and they change how start order and shutdown behave.

By , Founder at SeaGit

·

Key takeaways

  • A regular init container must exit successfully before the next init container and the app containers start. It is for setup work, not for long-running services.
  • A native sidecar is an entry in initContainers with restartPolicy: Always. It starts before the next init container, keeps running with the app, and is restarted if it fails.
  • The SidecarContainers feature gate is stable and locked on in current releases. It was first available in v1.28 and on by default from v1.29.
  • Native sidecars stop after the main containers have stopped, which fixes the Job problem where a helper in containers keeps the pod running forever.

Init containers: run to completion, in order

Init containers are declared under spec.initContainers. The kubelet runs them one at a time, in the order listed. Each one must succeed before the next starts. When all of them have finished, the app containers start. If an init container fails, the kubelet restarts it according to the pod restartPolicy, and the app does not start.

yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-api
spec:
  initContainers:
  - name: wait-for-db
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z postgres 5432; do sleep 2; done']
  - name: run-migrations
    image: my-api:1.4.2
    command: ['./migrate', '--up']
  containers:
  - name: app
    image: my-api:1.4.2
    ports:
    - containerPort: 8080
Two init containers run in order, then the app starts. Neither init container can serve traffic or run a readiness probe.

Regular init containers do not support lifecycle hooks, liveness, readiness or startup probes. They are one-shot work: wait for a dependency, run a migration, fetch a file into a shared volume, or fix ownership on a mount.

Sidecar containers: the helper that stays

A sidecar runs alongside the main application for its whole life. A log shipper, a service proxy, a secrets refresher or a config reloader are typical examples. Before native support, the usual way to run one was to list it in spec.containers next to the app. That works, but it has three gaps.

  • There is no start order. The sidecar and the app start at about the same time, so a proxy may not be listening when the app makes its first call.
  • There is no shutdown order. The sidecar can be stopped before the app has finished flushing logs or draining connections.
  • A helper in containers keeps a Job pod alive. The Job never completes because the sidecar never exits, even after the main work is done.

Native sidecars close those gaps. The kubelet starts them before the app and stops them after the app, and their lifecycle is independent of the regular init containers.

Native sidecars: restartPolicy Always in initContainers

To make a sidecar, put it under initContainers and set its restartPolicy to Always. The field is valid only on init containers, and it marks the entry as a sidecar rather than a one-shot step. The example below ships application logs from a shared volume through a sidecar, with a readiness probe on the sidecar.

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
  namespace: prod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-api
  template:
    metadata:
      labels:
        app: my-api
    spec:
      initContainers:
      - name: log-shipper
        image: fluent/fluent-bit:3.1
        restartPolicy: Always           # makes this a native sidecar
        readinessProbe:
          httpGet:
            path: /readiness            # replace with the health endpoint your shipper exposes
            port: 2020
          periodSeconds: 10
        volumeMounts:
        - name: logs
          mountPath: /var/log/app
      containers:
      - name: app
        image: my-api:1.4.2
        volumeMounts:
        - name: logs
          mountPath: /var/log/app
      volumes:
      - name: logs
        emptyDir: {}
The log-shipper starts before app. Its readiness probe feeds into the pod Ready state. Confirm the image, port and path against your shipper's documentation.

Because the sidecar is in the init list, it starts before any init container that follows it. If you put a migration after the sidecar, the migration runs while the sidecar is already up. Put setup that the sidecar must not see before it in the list.

Ordering, probes and shutdown in one table

The three kinds of container differ in what they guarantee. The table below sums up the behavior described in the Kubernetes Sidecar Containers and Init Containers pages.

QuestionRegular initNative sidecarIn containers list
Runs for the pod lifetime?No, runs to completionYesYes
Starts before the app?YesYes, and before later init containersNo ordering guarantee
Readiness probeNot supportedSupported, feeds pod ReadySupported
Stopped after the app?Not applicableYes, after main containers stopNot guaranteed
Keeps a Job pod running?NoNo, the kubelet stops itYes, if it never exits

Probes and the started status

A native sidecar counts as started when its process runs, or when its startupProbe succeeds, if one is set. The kubelet then moves on to the next init container. Use a startup probe for a sidecar that takes time to listen, so the app does not start before its proxy is up.

Versions and checking your cluster

Native sidecars were first available in Kubernetes v1.28, behind the SidecarContainers feature gate. The feature has been on by default since v1.29. The Kubernetes page for the feature now describes it as stable since v1.33, and the gate is locked, so setting it has no effect. Clusters on an older minor release may need the gate enabled explicitly, and the Kubernetes version policy covers the support window for each minor release.

bash
# Server version of the cluster you are connected to
kubectl version -o json | grep -A2 serverVersion

# On EKS, the control plane version for a named cluster
aws eks describe-cluster --region us-east-1 --name prod-cluster \
  --query cluster.version --output text
Check the server version, not the client version. A newer kubectl does not mean a newer cluster.

If you run an older release and cannot upgrade, keep the sidecar in containers and accept the start-order gap. Add a small wait loop in the app or an init container that checks the proxy before the app starts.

Choosing between an init container and a sidecar

Ask one question: does the helper need to finish, or does it need to keep running? If it must finish before the app can start, it is an init container. If it must run for as long as the app runs, it is a sidecar, and on a current cluster it should be a native sidecar.

  • Use an init container to wait for a dependency, run a schema migration, or prepare files in a volume.
  • Use a native sidecar for a log shipper, a service proxy, a certificate or secret refresher, or a config reloader that must run beside the app.
  • Use a plain container in containers when the helper does not need ordering and the pod never needs to complete, such as a metrics exporter that you are happy to start in any order.
  • Use a native sidecar, not a plain container, for any helper inside a Job or CronJob pod that must not block completion.

The same pod can mix all three kinds. Order the initContainers list as the start sequence you want, put the one-shot steps and the native sidecars together in that list, and keep the app in containers.

Frequently asked questions

Sidecar vs Init Containers: Native Sidecar Setup: FAQ

What is the difference between an init container and a sidecar container?

An init container runs to completion before the app starts and is not running during the app life. A sidecar runs alongside the app for the whole life of the pod. A native sidecar is declared as an init container with restartPolicy Always.

Which Kubernetes version added native sidecar containers?

The feature first appeared in v1.28 behind the SidecarContainers feature gate, and it has been on by default since v1.29. It is stable in v1.33 and the gate can no longer be turned off.

Does a sidecar in the containers list stop a Job from completing?

It can. A container in containers keeps the pod running until it exits, so a helper that never exits keeps the Job from completing. A native sidecar is stopped by the kubelet once the main containers have finished.

Can an init container have a readiness probe?

A regular init container cannot. A native sidecar, declared with restartPolicy Always, can have a readiness probe, and its result contributes to the pod Ready state.

Sources

Checked 11 October 2026.

  1. Kubernetes: Sidecar Containers — The v1.28 introduction, the v1.29 default, the stable and locked state from v1.33, probes and shutdown order.
  2. Kubernetes: Init Containers — Sequential run-to-completion behavior and the probe limits on regular init containers.
  3. Kubernetes: Pod Lifecycle — Pod phases, restart policy and termination order.
  4. AWS: Connect kubectl to an EKS cluster by creating a kubeconfig file — Connecting to the cluster before you check its server version.

Read next