skip to content

Sidecar & Ambient Data Plane

This is how Istio actually gets into the request path: an istio-proxy container injected next to your app, with iptables rules that quietly redirect the pod's traffic through it — or, in ambient mode, a per-node ztunnel and optional waypoint proxy instead. Interviewers ask because the answer reveals whether you understand the mesh's real cost: an extra container per pod, startup ordering races, and a mesh-wide restart every upgrade, versus ambient's cheaper L4 path that gives up per-pod isolation.

on this pageshow

questions

6

After Istio injection, a Kubernetes pod that declares a single application container shows READY 2/2. What is the second container, and how does the application's traffic end up flowing through it without any change to the application code?

level: juniorimportance: must knowfreq 70%

answer

  1. two containers, only one is yours
  2. added when the pod object is created
  3. rules live in the pod's network namespace
  4. app code and app config unchanged
  5. outbound 15001, inbound 15006

basics

~20 s

The second container is istio-proxy, an Envoy-based sidecar added by Istio's injection webhook when the pod is created. Injected rules in the pod's own network namespace redirect the pod's inbound and outbound TCP traffic through that proxy, so application code never changes.

solid answer

~50 s

The extra container is `istio-proxy` — an Envoy process plus Istio's `pilot-agent`, added to the pod spec by Istio's mutating admission webhook at pod-creation time, not by anything the application does. Alongside it Istio injects an `istio-init` container (or, with the Istio CNI plugin, does the equivalent from the node) that writes traffic-capture rules into the pod's network namespace. Those rules redirect outbound TCP connections from the app to the proxy's outbound port and inbound connections to its inbound port, so the app keeps dialling `http://orders:8080` and keeps listening on its normal port while every byte actually crosses Envoy. That is what makes routing, retries, telemetry and mTLS available without recompiling anything. The cost is one more container's memory and CPU in every pod, and a restart of every pod whenever the proxy is upgraded.

code

bash · 6 lines
bash
# List the containers Kubernetes actually created for a meshed pod
kubectl get pod my-app-5f7c9d8b6-2xk4t \
  -o jsonpath='{range .spec.initContainers[*]}init:{.name}{"\n"}{end}{range .spec.containers[*]}{.name}{"\n"}{end}'

# Read the sidecar's own logs when a meshed request misbehaves
kubectl logs my-app-5f7c9d8b6-2xk4t -c istio-proxy --tail=50

go deeper

for a junior

Be able to name the container as istio-proxy, say it was added automatically when the pod was created, and state plainly that the app's traffic is redirected into it without any code change.

for a middle

Explain the mechanism: a mutating admission webhook rewrites the pod spec, and capture rules in the pod's shared network namespace redirect inbound and outbound TCP to the proxy's ports. Mention that only TCP is captured.

for a senior

Show that you treat the proxy as a real hop in production — its logs and metrics are the first place you look, the peer address the app sees is local, and excluded ports exist for dependencies that break behind it.

for a principal

Frame the per-pod proxy as a cost model: memory multiplied by pod count, a mesh-wide restart per upgrade, and an extra failure domain in every request path. Be ready to say when that price is worth paying and when a shared gateway is enough.

## What the two containers are A meshed pod contains the container you wrote plus `istio-proxy`. `istio-proxy` runs two processes: **Envoy**, the actual proxy that moves bytes, and **pilot-agent**, a small supervisor that bootstraps Envoy, fetches its configuration and workload certificate from the `istiod` control plane, and exposes health and metrics endpoints. `kubectl get pod` counts both containers, so a one-container Deployment reports `2/2` once it is meshed. Most pods also briefly run a third container that is already finished by the time you look: an init container named `istio-init`, whose only job is to program the pod's traffic-capture rules. If the cluster runs the Istio CNI plugin instead, that init container is absent and the same rules are written from the node during pod network setup. ## How the container got there Nothing at runtime adds a container to a running pod. Istio registers a **mutating admission webhook** with the Kubernetes API server; when a pod object is created in a namespace that is in scope for the mesh, the API server calls the webhook, and the webhook returns a modified pod spec with `istio-proxy` (and `istio-init`) appended. That is why: - pods created *before* the namespace was labelled are not meshed until they are recreated; - you can see the sidecar in `kubectl get pod -o yaml` even though it appears in no manifest in your repository; - `istioctl kube-inject` can produce the same result offline, by rendering the injected spec for a manifest you then apply yourself. ## How traffic reaches the proxy The application is not configured with a proxy address, and it does not link an Istio library. Instead, capture rules are installed **in the pod's network namespace**, which every container in the pod shares: ``` outbound: app dials orders:8080 -> redirected to 127.0.0.1:15001 (Envoy) -> Envoy dials the real endpoint inbound: remote peer connects -> redirected to 127.0.0.1:15006 (Envoy) -> Envoy connects to the app's port ``` Because the redirection lives in the pod's network namespace, it affects only this pod — never other pods on the node, and never the node's own traffic. The proxy runs under its own UID, and traffic originating from that UID is exempted so Envoy's own outbound connections are not redirected back into itself in an infinite loop. Some traffic is deliberately left alone. Istio only captures TCP; UDP (including DNS, unless DNS capture is switched on) passes straight through. Ports and CIDR ranges can be excluded with pod annotations such as `traffic.sidecar.istio.io/excludeOutboundPorts` and `traffic.sidecar.istio.io/excludeInboundPorts`, which is the usual escape hatch for a database driver or an agent that misbehaves behind a proxy. ## What the application sees From the application's point of view, nothing changed: it resolves the same service name, dials the same port, and listens where it always listened. What changed is who is on the other end of the socket. Because Envoy is now a real hop, several things become visible: - Every request produces proxy access logs and metrics without the app emitting them. - The peer address the app sees for inbound requests is the local proxy, not the remote client — original client identity arrives as forwarded headers or as the mesh identity of the peer. - Timeouts, retries and failures can now originate in the proxy rather than the app, which is why reading proxy logs is the first step when a meshed service starts behaving oddly. ## Why interviewers ask this The answer separates people who have run Istio from people who have read about service meshes. It exposes the mesh's real, unglamorous cost: an extra container's memory and CPU multiplied by every pod, a mesh-wide pod restart on every proxy upgrade, an extra process that must be alive before your app's first request can succeed, and a second place to look during every incident. Those trade-offs — not the feature list — are what the follow-up questions are usually about.

  • If nothing in our manifest mentions istio-proxy, why does it appear in the running pod?
    Istio registers a mutating admission webhook with the API server. When the pod object is created, the API server calls that webhook, which returns a pod spec with the proxy container added. The stored object therefore differs from the manifest you applied — which is also why controllers such as Deployments must create new pods for injection to take effect.
  • Does the sidecar capture UDP traffic as well as TCP?
    No. Istio's capture applies to TCP. UDP — including ordinary DNS lookups — bypasses the proxy unless you explicitly enable Istio's DNS capture. That is why a UDP-based dependency gets no mesh telemetry, no mesh mTLS and no mesh routing, and why people are sometimes surprised that only part of a pod's traffic shows up in mesh dashboards.
  • What is the operational cost of one extra container per pod?
    Memory and CPU per pod rather than per node, so cost scales with pod count; an extra hop of latency on every call; a proxy upgrade that requires restarting every meshed pod; and a second component to inspect in every incident. At a few hundred pods it is noise; at tens of thousands it becomes a capacity line item.

It is like a mail room installed inside your office rather than a new address: you keep posting letters exactly as before, but everything now passes a desk that can log, stamp, reroute or refuse it.

saying these in an interview costs you the question

  • Says the application must be rebuilt against an Istio client library
  • Describes the sidecar as a separate pod beside the app pod
  • Claims the app is configured to dial the proxy's address
  • Thinks the sidecar is added when the container starts, not at admission
  • Believes the pod's capture rules affect other pods on the node

context

open as a page

A newly created pod in a namespace you believe is part of the Istio mesh came up with no istio-proxy container. Explain how Istio decides which pods get a sidecar, and how you would find the reason this one did not.

level: middleimportance: must knowfreq 62%

basics

~20 s

Istio injects through a mutating admission webhook selected by namespace labels — istio-injection=enabled or istio.io/rev=<revision> — with the pod-level sidecar.istio.io/inject label as an override. Check those labels, whether the pod predates them, revision mismatch after an upgrade, host-network pods, and webhook availability.

open as a page

Istio can program a pod's traffic-capture rules either with an injected `istio-init` container or with the Istio CNI plugin. What does each approach do, and what changes about pod privileges and pod startup when you switch to the CNI plugin?

level: middleimportance: should knowfreq 34%

basics

~20 s

The istio-init container writes the pod's redirection rules from inside the pod, which requires NET_ADMIN and NET_RAW on every workload. The Istio CNI plugin writes the same rules from a node-level DaemonSet during pod network setup, so workload pods need no elevated capabilities.

open as a page

An Istio-injected service logs connection failures on its first few outbound calls immediately after each pod starts, then behaves normally. What ordering problem causes this, and what does Istio offer to fix it?

level: seniorimportance: should knowfreq 48%

basics

~20 s

The application container starts before istio-proxy is ready, so early outbound calls hit redirection rules pointing at a proxy that cannot yet serve them. Fix it with holdApplicationUntilProxyStarts, or by running istio-proxy as a Kubernetes native sidecar container, which Kubernetes starts first.

open as a page

Istio's ambient mode replaces per-pod sidecars with a per-node ztunnel and optional per-namespace waypoint proxies. For an existing sidecar-based Istio mesh, how would you decide whether to move to ambient, and what are you giving up?

level: principalimportance: should knowfreq 38%

basics

~20 s

Decide by what your mesh is actually used for. Ambient's per-node ztunnel gives mTLS and L4 authorization without a proxy in every pod, removing per-pod overhead and mesh-wide upgrade restarts. You give up per-pod isolation and accept a node-level shared component, plus a waypoint hop wherever you need L7.

open as a page

In a large Istio mesh, each injected istio-proxy's memory footprint and configuration-push time grow as unrelated services are added elsewhere in the cluster. Why does that happen, and what does Istio's `Sidecar` resource do about it?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

By default istiod sends every sidecar the configuration for every service in the mesh, so per-proxy memory and push cost scale with mesh size rather than with a workload's actual dependencies. A Sidecar resource narrows each proxy's visible scope to the namespaces and hosts it really calls.

open as a page