Istio Quick Reference

Istio Quick ReferenceChapter 05 of 10

Istio performance and tuning

As of Istio 1.31 · Edition 2026.11 · Release data checked on 30 October 2026

Sidecar sizing

Default requests 100m / 128Mi, limits 2 CPU / 1Gi: not prod-grade at high RPS.

Istio benchmark (1.24, 1000 RPS, 1 KB, 2 workers): sidecar ≈ 0.20 vCPU + 60 MB, waypoint ≈ 0.25 vCPU + 60 MB, ztunnel ≈ 0.06 vCPU + 12 MB.

Memory scales with the config the proxy holds: Sidecar/exportTo trim it.

Pilot tuning

PILOT_PUSH_THROTTLE: default 0 = auto, min(15 + 5 × GOMAXPROCS, 100). PILOT_DEBOUNCE_AFTER: default 100 ms. PILOT_DEBOUNCE_MAX: default 10 s. On push storms (many pod restarts) raise debounce; for faster convergence raise throttle together with istiod CPU.

Ambient mode (GA since 1.24)

No sidecar injection. ztunnel: node DaemonSet, L4 mTLS (HBONE). Waypoint: optional Envoy for L7, per namespace, service or pod (istio.io/use-waypoint). Opt-in: label istio.io/dataplane-mode=ambient (namespace or pod). 1.31: weighted waypoint canaries (istio.io/use-waypoint-canary + ...-canary-weight).

Not in ambient: EnvoyFilter on waypoints, the Sidecar resource, PeerAuthentication DISABLE (ignored). VirtualService on waypoints is alpha: use HTTPRoute, never mix both.

Saves RAM at high pod counts, costs complexity when debugging.

Jobs & CronJobs (sidecar lifecycle)

App before proxy ready → connection refused. App done, sidecar still running → the job hangs.

Native sidecar (K8s 1.33+ GA): proxy as an initContainer (restartPolicy: Always), proper lifecycle. Default since Istio 1.27: ENABLE_NATIVE_SIDECARS=auto, native only when every node runs kubelet ≥ 1.33 (mixed clusters during a node upgrade get the classic sidecar).

Per pod (1.24+, beats the mesh setting): sidecar.istio.io/nativeSidecar: "false" forces the classic sidecar, "true" forces native.

Pre-native: holdApplicationUntilProxyStarts against the start race, trap with POST :15020/quitquitquit on EXIT against shutdown hangs (fires on crash/signal too, not only on success).

# Pre-native fallback (without native sidecars):
metadata:
  annotations:
    proxy.istio.io/config: |   # start race
      { "holdApplicationUntilProxyStarts": true }
spec:
  containers:
  - name: worker
    command: ["/bin/sh","-c"]
    args:
    - |
      trap 'curl -fsS -XPOST localhost:15020/quitquitquit||true' EXIT
      ./run-task

Graceful drain: EXIT_ON_ZERO_ACTIVE_CONNECTIONS

Classic sidecar: on SIGTERM the sidecar only drains for terminationDrainDuration (default 5 s), then hard-exits, long-lived connections (gRPC streams, WebSockets, DB pools) are cut mid-flight (503/reset on rollout/scale-down).

Fix: EXIT_ON_ZERO_ACTIVE_CONNECTIONS=true (via proxyMetadata or proxy.istio.io/config): pilot-agent waits MINIMUM_DRAIN_DURATION (default 5 s), then polls active Envoy connections and stops the proxy as soon as they reach 0 instead of on a fixed timer; terminationDrainDuration no longer applies.

Caveat: if a connection hangs (client never closes), the proxy blocks until terminationGracePeriodSeconds → SIGKILL. Raise the grace period accordingly. Mesh-wide via meshConfig.defaultConfig.proxyMetadata.

Native sidecar (default since 1.27): kubelet stops the proxy only after the app has exited, so the race mostly applies to the classic mode.