Skip to main content
RunBook Academy

KubernetesXI · Init Containers and SidecarsInit containers and sidecars

Sidecar lifecycle, restart, and resource semantics

Advanced⏱ ~16 minkubectl

What you'll learn

  • Reason about native sidecar lifecycle from creation to termination
  • Understand restartPolicy: Always semantics for sidecars
  • Size resource requests and limits for native sidecars
  • Diagnose sidecar restart loops and resource pressure

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Native sidecars behave like main containers with one difference: ordering. This lesson covers the lifecycle semantics in detail — how sidecars are created, restarted, stopped — and the production discipline around resource sizing.

The native sidecar lifecycle

stateDiagram-v2
    [*] --> Init: Pod created
    Init --> Startup: sidecar starts
    Startup --> Ready: startupProbe passes
    Ready --> Running: main container starts
    Running --> SidecarFailed: sidecar crashes
    SidecarFailed --> Ready: sidecar restarts, passes startupProbe
    Running --> Terminating: Pod deleted
    Terminating --> MainTerm: SIGTERM to main
    MainTerm --> SidecarTerm: main exited, SIGTERM to sidecar
    SidecarTerm --> [*]

The states:

  1. Init: Pod created; kubelet starts the first init container (which is the first sidecar if it has restartPolicy: Always).
  2. Startup: sidecar’s process starts; kubelet runs the startupProbe (if defined).
  3. Ready: startupProbe passes; kubelet considers the sidecar ready and proceeds.
  4. Running: main containers start; the sidecar runs in parallel.
  5. SidecarFailed: sidecar’s process exits or crashes. With restartPolicy: Always, the kubelet restarts the sidecar (subject to backoff).
  6. Terminating: Pod is being deleted.
  7. MainTerm: SIGTERM sent to main containers; they drain and exit.
  8. SidecarTerm: after main containers exit, SIGTERM sent to sidecars (in reverse order).

RestartPolicy: Always for sidecars

Native sidecars must have restartPolicy: Always. The kubelet:

  • Restarts the sidecar if its process exits.
  • Applies exponential backoff on repeated failures (CrashLoopBackOff).
  • Increments the sidecar’s restartCount in initContainerStatuses[*].restartCount.
kubectl get pod web-7c8 -o jsonpath='{.status.initContainerStatuses[*].restartCount}'
# 0 0 3

Three init containers; the third has been restarted 3 times. The kubelet is in CrashLoopBackOff for that sidecar.

Resource requests and limits

Native sidecars have separate resource fields:

initContainers:
- name: sidecar
  image: sidecar:1.0
  restartPolicy: Always
  resources:
    requests:
      cpu: 50m
      memory: 64Mi
    limits:
      cpu: 200m
      memory: 128Mi

The kubelet and scheduler account for these resources:

  • Scheduler: the Pod’s effective request is the sum of the largest init container’s request and the main containers’ requests.
  • Kubelet: enforces the limits via cgroups; throttles CPU, OOMKills memory.

For a Pod with:

  • sidecar: requests cpu=50m, memory=64Mi
  • main: requests cpu=500m, memory=512Mi

The Pod’s effective requests are cpu=550m, memory=576Mi. The scheduler reserves this on the chosen node.

Sidecar QoS classes

The Pod’s QoS class (Part XIII) is determined by the resource requests and limits of all containers (including native sidecars). A Pod with a BestEffort sidecar is BestEffort overall.

# Both containers have requests and limits: Burstable
initContainers:
- name: sidecar
  restartPolicy: Always
  resources:
    requests: {cpu: 50m, memory: 64Mi}
    limits:   {cpu: 200m, memory: 128Mi}
containers:
- name: main
  resources:
    requests: {cpu: 500m, memory: 512Mi}
    limits:   {cpu: 1, memory: 1Gi}

This Pod is Burstable (different requests vs limits). If both had matching requests and limits, it would be Guaranteed.

A Pod with a BestEffort sidecar (no requests/limits) is BestEffort and evicted first under node pressure. The main container’s QoS doesn’t matter — the Pod’s QoS is the weakest.

Startup probes on sidecars

A sidecar can declare a startupProbe:

initContainers:
- name: sidecar
  image: sidecar:1.0
  restartPolicy: Always
  startupProbe:
    httpGet:
      path: /healthz/ready
      port: 9090
    failureThreshold: 30
    periodSeconds: 2

The kubelet waits for the startupProbe to succeed before considering the sidecar ready and starting the next init container or main container.

For a service mesh sidecar:

startupProbe:
  httpGet:
    path: /healthz/ready
    port: 15021
  failureThreshold: 30
  periodSeconds: 2

Istio’s readiness endpoint is /healthz/ready on port 15021. The sidecar needs to be ready (proxy configured, xDS connected) before the main container starts.

Production patterns

Logging sidecar:

initContainers:
- name: log-shipper
  image: fluent-bit:3.0
  restartPolicy: Always
  resources:
    requests: {cpu: 50m, memory: 64Mi}
    limits:   {cpu: 200m, memory: 128Mi}
  volumeMounts:
  - name: logs
    mountPath: /var/log/app
containers:
- name: app
  image: app:1.0.0
  resources:
    requests: {cpu: 500m, memory: 512Mi}
    limits:   {cpu: 1, memory: 1Gi}
  volumeMounts:
  - name: logs
    mountPath: /var/log/app
volumes:
- name: logs
  emptyDir: {sizeLimit: 1Gi}

The log shipper reads from the shared emptyDir volume. The sizeLimit prevents the app from filling the node’s disk.

Service mesh sidecar:

initContainers:
- name: istio-proxy
  image: istio/proxyv2:1.20
  restartPolicy: Always
  resources:
    requests: {cpu: 100m, memory: 128Mi}
    limits:   {cpu: 500m, memory: 256Mi}
  startupProbe:
    httpGet: {path: /healthz/ready, port: 15021}
    failureThreshold: 30
    periodSeconds: 2
containers:
- name: app
  image: app:1.0.0

The Istio proxy is sized for its workload; the startup probe ensures it’s ready before the main container starts.

Metrics exporter sidecar:

initContainers:
- name: exporter
  image: prom/node-exporter:1.7
  restartPolicy: Always
  ports:
  - containerPort: 9100
  resources:
    requests: {cpu: 50m, memory: 32Mi}
    limits:   {cpu: 100m, memory: 64Mi}
containers:
- name: app
  image: app:1.0.0

A node-exporter-style sidecar that exposes metrics on its own port. Service mesh / Prometheus scrapes the sidecar directly.

Diagnosing sidecar issues

Sidecar not ready (Pod stuck in Pending):

kubectl describe pod web-7c8
# Events:
#   ... reason: Failed   ... message: container "sidecar" is not ready

The sidecar’s startupProbe is failing. Check the sidecar’s logs:

kubectl logs web-7c8 -c sidecar --previous

Sidecar OOMKilled:

kubectl get pod web-7c8 -o jsonpath='{.status.initContainerStatuses[0].state.terminated.reason}'
# OOMKilled

The sidecar exceeded its memory limit. Increase the limit or reduce the sidecar’s memory usage.

Sidecar crash loop:

kubectl get pod web-7c8 -o jsonpath='{.status.initContainerStatuses[*].restartCount}'
# 12

The sidecar is repeatedly crashing. Check logs and exit reasons. Common causes:

  • Configuration error (the sidecar cannot start without valid config).
  • Dependency unreachable (the sidecar’s startup blocks on a missing service).
  • Bug in the sidecar’s startup logic.

Sidecar termination ordering wrong:

Verify the sidecar is a native sidecar:

kubectl get pod web-7c8 -o jsonpath='{.spec.initContainers[0].restartPolicy}'
# Always

If the restartPolicy is missing, the kubelet treats the init container as a one-shot setup container; termination ordering is not enforced.

Cross-course references

  • The Linux course part VI-Linux-Processes covers process lifecycle and cgroups; sidecar resources are the cluster-level equivalent.
  • The Docker course part XXX-Docker-Lifecycle covers container lifecycle; sidecar lifecycle is the cluster-level extension.
  • The Ansible course part XXXV-Ansible-Scripting covers service restart discipline; sidecar restart is the cluster-level equivalent.

Quiz

Knowledge check · 4 questions

  1. Q1. What happens when a native sidecar's process exits unexpectedly?

  2. Q2. A Pod with a BestEffort sidecar (no requests/limits) has Burstable QoS because the main container has requests/limits.

  3. Q3. A service mesh sidecar is being OOMKilled repeatedly. The Pod is in CrashLoopBackOff. Walk through the diagnosis and the fix.

    Pod `web-7c8` with Istio sidecar. The sidecar is OOMKilled at startup. `kubectl get pod web-7c8 -o jsonpath='{.status.initContainerStatuses[0].state.terminated}'` shows `reason: OOMKilled, exitCode: 137`. The sidecar's memory limit is 128Mi.

  4. Q4. Why does a sidecar's resource consumption matter for the main container?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Size sidecar resources for their actual workload. A sidecar that does heavy work (log shipping, parsing) needs real resources.
  • Use restartPolicy: Always for native sidecars. This is what makes them native sidecars (not sleep-infinity workarounds).
  • Add startup probes for service mesh sidecars. The mesh must be ready before the main container starts.
  • Set the Pod’s QoS correctly. A BestEffort sidecar makes the whole Pod BestEffort, regardless of the main container.
  • Monitor sidecar resource usage with Prometheus. Sidecars can be invisible resource consumers; track their CPU and memory.