Skip to content
SRE & DevOpsDeep Dive Published Updated 5 min readViews unavailable

Kubernetes Native Sidecars: Startup Ordering and Reverse Shutdown

Model long-running helper containers as restartable init containers, then verify startup order, probes, Job completion, resource requests, and termination behavior.

Kubernetes native sidecars use an init-container entry with container-level restartPolicy set to Always. Unlike a conventional init container, the process stays running alongside the application after initialization. That API shape gives the kubelet explicit lifecycle information: sidecars can start before later init containers and application containers, and they can be stopped after the application workload no longer needs them.

spec:
  initContainers:
    - name: log-forwarder
      image: example.invalid/log-forwarder:1.0
      restartPolicy: Always
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
  containers:
    - name: app
      image: example.invalid/app:1.0

The image names are placeholders, and the readiness endpoint must actually be implemented by the chosen sidecar. A readiness probe on a native sidecar contributes to Pod readiness. Startup ordering matters when the application requires a local proxy, credential helper, or log receiver before it can function; declare dependencies and probes instead of relying on a startup race.

Shutdown order is part of the contract

During Pod termination, Kubernetes postpones sidecar shutdown until the main application containers have stopped, then terminates sidecars in reverse declaration order. This lets a proxy or log shipper remain available while the application drains and exits. It does not grant infinite cleanup time: the Pod’s termination grace period still bounds shutdown, and a sidecar that outlives the remaining budget can be killed.

For Jobs, a native sidecar does not by itself prevent completion after the main container finishes. That is a major behavioral difference from legacy sidecars implemented as ordinary app containers, which could keep a Job alive unless they had a separate exit mechanism.

Validate the cluster and the resource model

The SidecarContainers feature gate is enabled by default starting with Kubernetes 1.29, but check the actual API server, kubelet, and deployment policy before relying on this field. Older clusters or managed offerings may reject or handle the field differently. Validate the rendered manifest with server-side dry run against the target cluster, then inspect Pod events and container states through startup, readiness failure, application crash, and deletion.

Sidecars share the Pod network and may share volumes with the application. Their requests and limits affect effective Pod scheduling and QoS; they are not free helpers. Size them from observed load, and ensure shutdown hooks flush only what can fit in the grace-period budget. Native lifecycle semantics improve coordination, but they do not replace an explicit service contract between the helper and the application.

The feature first appeared in Kubernetes 1.28, became enabled by default in 1.29, and is stable from 1.33. The current upstream documentation describes the feature gate as locked at stable status. Keep the version history explicit in runbooks: a manifest accepted by a recent cluster may be rejected by an older API server, and admission policies or deployment tooling can impose additional constraints. A server-side dry run against the cluster that will receive the workload is more useful than relying only on a local schema file.

Startup ordering depends on the started condition

Regular init containers run to completion in list order. A restartable init container marked as a sidecar starts and remains running, but Kubernetes does not treat “the container process exists” as proof that its service is ready for dependent initialization. Without a startupProbe, the kubelet can mark the sidecar as started once its process is running; with one, the successful startup probe controls that transition. Only then does the kubelet proceed to the next init-container entry. This gives a later initialization step a defined dependency point, but the sidecar still needs its own retry and failure behavior.

A readinessProbe on the sidecar contributes to the Pod’s readiness. That is useful when the helper must be ready before the Pod should receive traffic, but it can also make the whole Pod unready if the helper is misconfigured or its dependency is unavailable. Keep probe endpoints local and truthful, set startup/readiness thresholds for the real startup profile, and avoid using liveness checks for transient dependencies that should not restart the application. A probe reports a state; it does not repair a broken proxy, rotate a stale credential, or guarantee that the application can use the sidecar correctly.

Plan resources and shutdown as Pod-level behavior

The scheduler evaluates effective Pod requests, not an isolated “free” sidecar budget. For each resource, the effective request accounts for Pod overhead and compares the steady-state sum of application and restartable sidecar containers with the effective init-container request. A helper that looks small at runtime can still affect placement during initialization, and all containers contribute to the Pod’s resource and quality-of-service model. Measure sidecar CPU and memory under realistic load, include burst and restart behavior, and check the rendered Pod’s effective requests before increasing replica count.

During deletion, Kubernetes waits for application containers to stop before terminating native sidecars, then stops sidecars in reverse declaration order. This preserves dependencies such as a proxy used while the app drains or a log collector needed while the app flushes. The Pod’s termination grace period still bounds the full sequence. If the application uses all available time, a sidecar may receive a stop signal and then be killed before its own cleanup completes; plan sidecar shutdown to be short, idempotent, and safe to interrupt. Do not assume reverse ordering gives any container unlimited time.

Validate behavior instead of only YAML shape

Use a test workload with a visible dependency, such as an app that waits for a local proxy health endpoint, and observe startup, readiness, failure, and deletion. Validate against the actual control plane with kubectl apply --dry-run=server -f workload.yaml, inspect events with kubectl describe pod, and inspect each container separately with kubectl logs POD -c CONTAINER. For Jobs, confirm that the main process can finish while its native sidecar is still running; this is one of the practical differences from an ordinary application-container sidecar that can keep a Job active.

When a Pod remains unready, check the sidecar’s started state, startup-probe results, readiness-probe results, and application dependency logs separately. When shutdown is slow, compare app and sidecar exit times with the grace deadline and check whether the sidecar is declared under initContainers with restartPolicy: Always. These checks catch the common migration errors: using the old app-container layout, relying on a sleep instead of a readiness contract, forgetting that readiness affects the whole Pod, or under-sizing resources because the sidecar looked idle in a narrow test.

Related:

Sources:

Comments