KubernetesXI · Init Containers and SidecarsInit containers and sidecars
Sidecar lifecycle, restart, and resource semantics
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
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:
- Init: Pod created; kubelet starts the first init
container (which is the first sidecar if it has
restartPolicy: Always). - Startup: sidecar’s process starts; kubelet runs the startupProbe (if defined).
- Ready: startupProbe passes; kubelet considers the sidecar ready and proceeds.
- Running: main containers start; the sidecar runs in parallel.
- SidecarFailed: sidecar’s process exits or crashes.
With
restartPolicy: Always, the kubelet restarts the sidecar (subject to backoff). - Terminating: Pod is being deleted.
- MainTerm: SIGTERM sent to main containers; they drain and exit.
- 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
restartCountininitContainerStatuses[*].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-Processescovers process lifecycle and cgroups; sidecar resources are the cluster-level equivalent. - The Docker course part
XXX-Docker-Lifecyclecovers container lifecycle; sidecar lifecycle is the cluster-level extension. - The Ansible course part
XXXV-Ansible-Scriptingcovers service restart discipline; sidecar restart is the cluster-level equivalent.
Quiz
Knowledge check · 4 questions
Q1. What happens when a native sidecar's process exits unexpectedly?
Q2. A Pod with a BestEffort sidecar (no requests/limits) has Burstable QoS because the main container has requests/limits.
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.
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: Alwaysfor 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.