KubernetesXI · Init Containers and SidecarsInit containers and sidecars
Migrating from annotation-based sidecars to native sidecars
What you'll learn
- Plan and execute a migration from annotation-based sidecars to native sidecars
- Verify that workloads receive native sidecars after migration
- Test termination ordering and other lifecycle properties
- Roll back the migration if it causes issues
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
Migrating from annotation-based sidecars to native sidecars is a cluster-wide operation. This lesson walks through the migration steps, the verification at each stage, and the rollback plan if the migration causes issues.
Migration prerequisites
Before migrating, verify:
- Kubernetes version: cluster is 1.28 or later (native sidecars GA in 1.28).
- Service mesh version: the mesh supports native sidecars (Istio 1.20+, Linkerd 2.14+, etc.).
- Mutating webhook configuration: the mesh’s sidecar injector is configured to emit native sidecars.
- Test environment: the migration is tested in staging first.
kubectl version
# Server Version: v1.30.x
istioctl version
# 1.20.x or later
Step 1: Configure the mesh
For Istio:
istioctl install --set values.global.proxy.image=... \
--set values.global.proxy.nativeSidecar=true \
--set values.global.proxy.lifecycle.imagePullPolicy=IfNotPresent
Or via the IstioOperator:
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
meshConfig:
defaultConfig:
proxyMetadata:
ISTIO_META_ENABLE_HBONE: "true"
components:
proxy:
nativeSidecar: true
For Linkerd, the sidecar injection is already native-sidecar
compatible in recent versions; verify with linkerd check.
For other service meshes, consult the mesh’s documentation.
Step 2: Restart workloads
After enabling native sidecars in the mesh, restart all workloads so they receive the new sidecar format:
kubectl rollout restart deployment -A
# or per-namespace
kubectl rollout restart deployment -n team-a-prod
This triggers a rolling update; new Pods are created with the new sidecar format. Old Pods are terminated; new Pods have native sidecars.
Step 3: Verify the migration
For each namespace, verify that the sidecar is now a native sidecar:
kubectl get pod web-7c8 -o jsonpath='{.spec.initContainers[0].restartPolicy}'
# Always
If the output is empty, the sidecar is still a sleep-infinity workaround. Check the mesh’s configuration.
Other checks:
# Sidecar has a startup probe
kubectl get pod web-7c8 -o jsonpath='{.spec.initContainers[0].startupProbe}'
# should not be empty
# Sidecar resources are set
kubectl get pod web-7c8 -o jsonpath='{.spec.initContainers[0].resources}'
# should not be empty
The native sidecar has explicit restartPolicy: Always,
startupProbe, and resources. The sleep-infinity workaround
has none of these.
Step 4: Test termination ordering
After the migration, test that the new ordering works:
kubectl delete pod web-7c8 --grace-period=30
# Watch the termination sequence
kubectl get pod web-7c8 -w
The expected sequence:
- Pod’s
deletionTimestampis set. - Main container receives SIGTERM.
- Main container drains, exits.
- Sidecar receives SIGTERM (after main exits).
- Sidecar drains, exits.
- Pod is removed.
For the sleep-infinity workaround, SIGTERM is sent to main and sidecar in parallel; the main may exit while the sidecar is being killed.
sequenceDiagram
participant K as Kubelet
participant M as Main
participant S as Sidecar
rect rgb(200, 240, 200)
Note over M: Native sidecar (after migration)
K->>M: SIGTERM
M-->>M: drain, exit
K->>S: SIGTERM (after main)
S-->>S: drain, exit
end
rect rgb(240, 200, 200)
Note over M: Sleep-infinity workaround (before)
K->>M: SIGTERM (parallel)
K->>S: SIGTERM (parallel)
M-->>M: drain (sidecar may already be dead)
S-->>S: dies mid-drain
end
Step 5: Monitor for issues
After the migration, watch for:
- Pods stuck in Pending: native sidecars may take longer to start if their startupProbe is more strict than the sleep-infinity workaround.
- Sidecar restart loops: the sidecar may crash if its configuration is incompatible with the new format.
- Memory pressure: native sidecars with explicit resources may use more memory than the workaround.
# Pods stuck in Pending with sidecar-related events
kube_pod_container_status_waiting_reason{reason="ContainerCreating"}
# Sidecar restart count
kube_pod_init_container_status_restarts_total
Rollback procedure
If the migration causes issues:
- Revert the mesh configuration: disable native sidecar injection, restore the sleep-infinity workaround.
- Restart workloads:
kubectl rollout restart deployment -Ato trigger re-injection with the workaround. - Verify the rollback: check that the new Pods have
the workaround (no
restartPolicyon the init container).
istioctl install --set values.global.proxy.nativeSidecar=false
kubectl rollout restart deployment -A
The rollback is the inverse of the migration. The old workaround Pods are not graceful (parallel SIGTERM), but they work.
Migration in stages
For a large cluster, do the migration in stages:
- One namespace first: pick a low-risk namespace; run the migration; verify; document issues.
- Cluster-wide rollout: enable native sidecars cluster- wide; rollout restart all workloads.
- Verification: monitor for 24-48 hours; verify no regressions.
# Stage 1: one namespace
kubectl label namespace team-a-staging istio-injection=enabled
istioctl install --set ... --set values.global.proxy.nativeSidecar=true
kubectl rollout restart deployment -n team-a-staging
# Stage 2: cluster-wide
kubectl label namespace --all istio-injection=enabled
kubectl rollout restart deployment -A
Production patterns
Pre-migration checklist:
- Cluster is 1.28+
- Service mesh version supports native sidecars
- Mesh is configured for native sidecar injection
- Test environment: migration verified in staging
- Rollback procedure documented and tested
- Monitoring in place (Pending Pods, sidecar restarts)
- Communication: users notified of the change
Post-migration checklist:
- All workloads have native sidecars
(
restartPolicy: Alwayson init container) - No Pods stuck in Pending
- Sidecars have startup probes
- Sidecars have explicit resources
- Termination ordering verified under load
- No increase in 5xx errors during the migration window
Cross-course references
- The Linux course part
IX-Linux-Bootcovers service restart patterns; the migration is the cluster-level equivalent. - The Docker course part
XXIX-Docker-Buildcovers image upgrades; the mesh upgrade is the cluster-level equivalent. - The Ansible course part
XXXV-Ansible-Scriptingcovers rolling restart discipline; the staged rollout is the same idea.
Quiz
Knowledge check · 4 questions
Q1. What is the first prerequisite for migrating from sleep-infinity sidecars to native sidecars?
Q2. After enabling native sidecars in the mesh, all workloads must be rolled out (or recreated) to receive the new sidecar format.
Q3. An operator enables native sidecars on the cluster. After rollout restart, 30% of Pods are stuck in Pending with the sidecar's startupProbe failing. Diagnose and remediate.
Cluster: Kubernetes 1.30, Istio 1.20 with native sidecars enabled. After `kubectl rollout restart deployment -A`, 30% of Pods are in Pending with `kubectl describe pod` showing `Init:CrashLoopBackOff` or `sidecar's startupProbe failing`. The startupProbe is `httpGet /healthz/ready on port 15021`.
Q4. Why is staging the migration in stages (one namespace first, then cluster-wide) the right approach?
Passing score: 75%. Answers are checked in this browser.
Production discipline
- Test the migration in staging first. Verify termination ordering, sidecar restarts, and resource usage before going cluster-wide.
- Stage the rollout by namespace. Reduce blast radius; catch issues early.
- Pre-stage the rollback. Document and test the rollback procedure before the migration.
- Verify each workload’s sidecar after migration.
restartPolicy: Alwayson the init container is the marker. - Monitor for 24-48 hours after migration. Pods stuck in Pending, sidecar restart loops, memory regressions.