Stop reading whenever you have enough
Sidecar injection, in twelve questions.
Each one is the question the previous answer makes you ask. The ones that matter come with the command, because that is the part you meet at work.
Not technical? The first three questions are written for you, and they are enough to follow any conversation about this.
- Rungs
- 12
- Hands on
- 6
- Read
- ~8 min
What it is
01–03Nobody adds the helper to their pods by hand. The cluster slips it in at creation, if you asked.
three minutes, no cluster needed · for anyone
- 01
How does the proxy get into my pod?
Not knowing this costs
The pod running in the cluster is not the pod you wrote.
- A webhook rewrites pods at creation time
- It adds the proxy container and an init step
- Your deployment YAML never mentions either
- 02
What decides which pods get rewritten?
Not knowing this costs
Injection decisions live in two places, and reviews check one.
- A label on the namespace opts everyone in
- An annotation on one pod opts it out
- Most specific wins, pod over namespace
- 03
When does the rewrite happen?
Not knowing this costs
A labelled namespace full of old pods is a mesh on paper only.
- Only at pod creation, never retroactively
- Labelling a namespace changes nothing running
- Existing pods need a restart to be meshed
Before you scroll on
0/3You should now be able to
The switches you will see
04–05One label on the namespace, one annotation on the pod, and a clear precedence.
the label, the annotation, and who wins · for whoever writes the manifests
- 04
What do the switches look like?
hands onNot knowing this costs
Forgetting the rollout restart is why the label seemed to do nothing.
- istio-injection: enabled, on the namespace
- sidecar.istio.io/inject: "false", on one pod
- Both are one line
shellopt in the room, opt out one chairkubectl label namespace payments istio-injection=enabled # then, in one Deployment's pod template only: # metadata: # annotations: # sidecar.istio.io/inject: "false" kubectl rollout restart deploy -n payments # existing pods re-create, meshedRevision based meshes use istio.io/rev: <revision> instead of the enabled label. Mixing the two labels on one namespace is a standing source of surprise. - 05
How does traffic end up inside the proxy?
Not knowing this costs
This redirect is why apps need zero changes, and why 1/1 pods bypass everything.
- The injected init step rewrites iptables
- All pod traffic redirects through the proxy ports
- The app cooperates by existing, nothing more
Before you scroll on
0/3You should now be able to
Proving it works
06–07Injected or not is visible in the pod spec, not a matter of opinion.
commands that answer yes or no · for whoever has to sign it off
- 06
Is this pod actually injected?
hands onNot knowing this costs
READY 2/2 is a hint. The spec is the proof.
- Read the containers off the running pod
- Expect istio-proxy, plus istio-init before it
shellinjected or not, in one linekubectl get pod payment-svc-7d9c-x2k4 -n payments \ -o jsonpath='{.spec.initContainers[*].name} | {.spec.containers[*].name}' # istio-init | payment-svc istio-proxy <- injected # | payment-svc <- not injectedOn clusters using native sidecars, istio-proxy appears in initContainers instead. Either way, its presence is the answer. - 07
Which pods in the namespace escaped the mesh?
hands onNot knowing this costs
The unmeshed pod list is the pre flight check STRICT rollouts skip.
- List pods without an istio-proxy container
- Every hit is invisible to your policies
shellthe gap listkubectl get pods -n payments -o json | jq -r ' .items[] | select([.spec.containers[].name] | index("istio-proxy") | not) | .metadata.name' # legacy-batch-9zk1 <- unmeshed, every policy blind to itRun this after enabling STRICT anywhere: each name on this list is a workload about to be refused by servers it called yesterday.
Before you scroll on
0/3You should now be able to
When it breaks
08–10Injection fails at the edges of the pod lifecycle: the first second, the last second, and upgrades.
the three lifecycle traps · for whoever gets paged
- 08
My app crashes on startup since joining the mesh. Why?
hands onNot knowing this costs
A race that only bites the fastest starting apps is a flaky mystery for weeks.
- The app started before its proxy was ready
- Its first outbound calls hit a half built redirect
- Hold the app until the proxy is up
manifestthe ordering fix# per pod annotation: metadata: annotations: proxy.istio.io/config: | holdApplicationUntilProxyStarts: true # or mesh wide, in meshConfig: # defaultConfig: # holdApplicationUntilProxyStarts: trueThe classic symptom is a connection refused burst in the first two seconds, then normality: apps that retry survive it, apps that crash on first failure do not. - 09
My Jobs never complete since joining the mesh. Why?
hands onNot knowing this costs
A CronJob that never completes fills the namespace with zombie pods.
- The app container exits, the proxy keeps running
- A pod with a running container is not complete
- Native sidecars fix this properly on modern clusters
shellthe modern fix, and the workaround# native sidecars: the proxy runs as an initContainer with # restartPolicy Always, and Kubernetes stops it when the job's # container finishes. Needs BOTH: # - Kubernetes 1.29+ # - Istio installed with ENABLE_NATIVE_SIDECARS=true # everywhere else: the job tells the proxy to quit as its last act curl -fsX POST http://127.0.0.1:15020/quitquitquitThe flag is the half people miss: a 1.29 cluster running a default Istio install still has the classic sidecar and still hangs. Check with the initContainers query from rung 5. The quitquitquit call belongs chained after the real work, as the job’s last command. - 10
The mesh upgraded. Are my pods on the new proxy?
hands onNot knowing this costs
A mesh upgrade without workload restarts upgraded nothing that carries traffic.
- No. Pods keep the proxy they were born with
- Every upgrade owes a rolling restart of workloads
- istioctl reports the version skew
shellfind the stragglersistioctl proxy-status | awk '{print $NF}' | sort | uniq -c # 412 1.24.2 # 38 1.23.1 <- born before the upgrade, still on the old proxyData plane more than one minor behind the control plane is outside the support skew. The stragglers list is the restart backlog, not an FYI.
Before you scroll on
0/3You should now be able to
Where it ends
11–12Injection is convenience, not enforcement. The difference is a security boundary.
the limits, where people get caught · for whoever reviews the platform
- 11
Is the injection label a security control?
Not knowing this costs
A threat model resting on a label rests on a suggestion.
- No. Anyone who can annotate a pod can opt out
- Injection is convenience for joining the mesh
- Refusing the unmeshed is STRICT, at the servers
- 12
What does enforcement actually look like?
Not knowing this costs
The mTLS ladder is where the suggestion becomes a wall.
- PeerAuthentication STRICT refuses unmeshed callers
- Admission policy can reject uninjected pods outright
- Label for convenience, STRICT for the guarantee
Before you scroll on
0/3You should now be able to
Go deeper
3 links, each earning its place.