You turned on Istio's mTLS and then watched CPU requests across the namespace climb. Nobody wrote down what that was actually going to cost before the security review demanded it. The honest numbers exist — in the mesh vendors' own documentation and in a couple of independent benchmarks — and they're specific enough to make the call for you, if you read past the slide that says "negligible overhead."

What the mTLS tax actually is

The overhead people mean when they say "mTLS tax" rarely comes from the TLS handshake. It comes from the proxy sitting in front of the application. Istio's documentation is explicit about the mechanism: the sidecar adds an authentication filter, a telemetry filter, and a metadata-exchange filter to every request, and each additional filter lengthens the path the request travels inside the proxy — which is where the added latency comes from. That's also why the proxy needs its own CPU and memory budget: it's doing real work on the data path, not just forwarding bytes.

Istio's sidecar, by the numbers

Istio's own benchmarks give you a number to plan capacity around — but check which version you're citing before you trust it. In Istio 1.24's official benchmark (HTTP/1.1, 1 KB payloads, 1,000 requests per second, 4 client connections, 2 proxy workers, mutual TLS on), a sidecar proxy with two worker threads uses about 0.20 vCPU and 60 MB of memory. A waypoint proxy in the same test — the L7 companion used in ambient mode — uses about 0.25 vCPU and 60 MB.

Older documentation tells a rougher story. Istio 1.22's docs put sidecar cost at roughly 0.5 vCPU per 1,000 requests per second — more than double the 1.24 figure at the same throughput, though the two weren't produced by an identical harness, so treat the gap as a real but imprecise trend rather than an exact multiplier. Istio 1.1's docs put the number even higher, around 0.6 vCPU per 1,000 rps, with about 50 MB of memory in a large namespace, and note that memory use doesn't scale with request rate because the proxy doesn't buffer the data passing through it.

One thing holds across all three versions: proxy memory tracks configuration state — the number of listeners, clusters and routes it holds — not traffic volume. A proxy in a mesh with thousands of services costs more in memory than one in a small mesh at the same request rate.

Ambient mode: what changes when you remove the sidecar

Istio's ambient mode replaces the per-pod sidecar with ztunnel, a proxy written in Rust and deliberately scoped down. Ztunnel handles only L3/L4 concerns — mTLS, authentication, L4 authorization, telemetry — and doesn't terminate or parse HTTP traffic at all. Anything needing L7 features, like HTTP-aware telemetry or load balancing, gets forwarded to a separate waypoint proxy instead. Istio's own architecture design document for ztunnel states the goal plainly: let teams get mTLS everywhere without adopting the rest of the mesh, and hold ztunnel to a tighter CPU, memory, latency and throughput budget than a sidecar carries. According to its source repository, ztunnel's mTLS runs on rustls, restricted to TLS 1.3 with AES-256-GCM or AES-128-GCM, with a FIPS-compliant "boring" build available for teams that need it.

Whether that design goal holds under load is where independent numbers matter more than architecture diagrams. A preprint benchmark supported by Red Hat Research, comparing Istio sidecar, Istio Ambient and Cilium under load, found Istio's sidecar had the largest latency increase of the three and Ambient the smallest. At the highest load tested in that benchmark — a target of 12,800 requests per second — the sidecar setup couldn't keep up: the load generator only achieved 6,868 requests per second, because added latency became the bottleneck. Cilium's CPU and latency numbers looked best of the three in that same study, but the paper is upfront that this partly comes from not encrypting traffic between pods on the same node — a security trade-off, not a free performance gain. Ambient had the best memory footprint of the three in that comparison, with sidecar-mode Istio worst on both CPU and memory. Treat the preprint's numbers as directional — it's one benchmark run, not yet a body of independent confirmation.

Linkerd: lighter proxy, a rotation problem to own

Linkerd takes a different default stance: mTLS turns on automatically for meshed traffic, but it isn't enforced. An unencrypted request between meshed pods gets opportunistically upgraded to mTLS rather than rejected, which means an accidental non-meshed hop degrades quietly instead of failing loudly — worth knowing before an audit asks you to prove mTLS is mandatory rather than default.

The other thing to check before leaning on Linkerd's "batteries included" mTLS: Linkerd's own documentation notes that the out-of-the-box trust anchor and identity-issuer certificates are self-signed with a one-year validity, and that while workload certificates rotate automatically, the identity issuer certificate and the trust anchor do not — someone has to rotate the root manually before it expires, and rotation requires manual intervention before expiry.

On raw cost, the clearest comparison comes from Buoyant, Linkerd's own maintainer — read it knowing a vendor is comparing itself favorably against a competitor, not a neutral third party. In Buoyant's own benchmark, at the highest throughput tested, Linkerd used about one-sixth the memory and 55% of the CPU that Istio's data plane used, while adding roughly a third of Istio's additional median latency and an eighth of its additional maximum latency. Directionally that lines up with the independent preprint above — sidecar Istio carrying the heaviest tail-latency cost of the group — though note that the Buoyant benchmark and the preprint used different methodologies and workloads, so the exact multipliers are not directly comparable.

The decision: what to actually run

Option Latency profile Resource cost Pick it when
Istio sidecar Highest tail-latency cost of the options discussed above Heaviest per-pod footprint; memory scales with mesh-wide config state You need full L7 policy on every hop and already run the sidecar model
Istio ambient (ztunnel) Lowest latency increase of the Istio options in the benchmarks above Lighter than sidecar; L7 cost only where you deploy a waypoint You want mTLS mesh-wide without paying the L7 tax on every pod
Linkerd Lower latency and resource cost than Istio sidecar in the benchmarks above Lightest control plane discussed here You want mTLS on by default and can staff manual trust-anchor rotation
App-level TLS No proxy in the path, so no filter chain to add to it No sidecar CPU/memory tax at all Your service count is small enough to hand-roll cert issuance and rotation without it becoming its own project

If mTLS is the only reason you're evaluating a mesh, ambient mode is the more honest match — that's the exact use case its own design document names. Reach for Linkerd instead when the lightest footprint matters more than avoiding a second control plane, and you're willing to own the trust-anchor rotation it doesn't automate. Skip the mesh entirely once your service count is small enough that app-level TLS doesn't become an ongoing maintenance project of its own — a mesh buys you mesh-wide policy and observability, not faster TLS, and paying its CPU and latency cost for a security property your own TLS config could give you is the version of this decision most teams get wrong.