Istio’s sidecar proxies, while essential for its powerful features, can become a significant bottleneck, consuming valuable CPU and memory and adding latency to every request.
Let’s see what that looks like in practice. Imagine a simple service productpage that calls details.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: productpage
spec:
hosts:
- productpage
http:
- route:
- destination:
host: details
port:
number: 9080
When a request hits productpage, it first goes to the productpage sidecar (Envoy proxy), then to the productpage application, then back to the productpage sidecar, then to the details sidecar, then to the details application, and finally back through the same proxy chain. Each hop adds overhead.
The core problem Istio solves is decoupling network concerns from application logic. Instead of each microservice needing to implement retry logic, circuit breaking, mTLS, tracing, and traffic management, these capabilities are offloaded to the Envoy sidecar. This allows developers to focus on business logic while operations teams manage the network behavior.
Internally, Envoy operates as a high-performance, asynchronous network proxy. When a request arrives at a pod, it’s intercepted by iptables rules injected by the Istio sidecar. These rules redirect ingress and egress traffic to the Envoy proxy running in the same pod. Envoy then applies the configured policies (e.g., routing, security, telemetry) before forwarding the request to the actual application container or out to another service’s sidecar.
The primary levers you have for tuning are within the Istio Operator configuration and the Envoy configuration itself, which can be influenced by Istio resources.
Istio Operator Configuration:
-
Sidecar Resource Limits: This is your first line of defense. By default, Istio often doesn’t set explicit resource requests and limits for the sidecar, allowing Kubernetes to assign arbitrary resources or relying on the node’s capacity. This can lead to resource contention.
To fix this, you can define specific
resourcesfor theistio-proxycontainer in yourIstioOperatorcustom resource:apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: components: pilot: k8s: env: - name: PILOT_SIDECACHE_ENABLED value: "true" # Enable sidecar proxy caching for config sidecarInjector: k8s: resources: requests: cpu: "50m" memory: "100Mi" limits: cpu: "200m" memory: "256Mi"This tells Kubernetes to allocate at least 50m CPU and 100Mi memory to the sidecar, and not let it exceed 200m CPU and 256Mi memory. This prevents the sidecar from starving the application and ensures predictable resource usage.
-
PILOT_SIDECACHE_ENABLED: Set this totruein the Pilot component’s environment variables in yourIstioOperator. By default, Pilot may not aggressively cache generated Envoy configurations. Enabling this allows Pilot to cache these configurations, reducing the CPU load on Pilot itself and speeding up the delivery of configuration updates to sidecars. This means sidecars spend less time waiting for configuration and more time processing traffic.
Envoy Configuration (Influenced by Istio Resources):
-
Stat Filter Configuration: Envoy generates a lot of metrics by default. While useful, this can add CPU overhead. You can selectively disable or reduce the collection of certain stats.
This is controlled via the
proxy.istio.io/configannotation on your Pods or throughProxyConfigresources. For instance, to disable stats collection for ingress listeners:apiVersion: v1 kind: Pod metadata: name: my-app-pod annotations: proxy.istio.io/config: '{"proxyMetadata":{"statsFilter": "ingress-listener-stats-off"}}' spec: containers: - name: my-app image: my-app-imageThis reduces the number of internal Envoy operations related to stat collection, freeing up CPU cycles for request processing.
-
Connection Pool Settings: For services with high concurrency, tuning connection pool settings can be critical. By default, Envoy might be too aggressive or too conservative with its connection pooling.
You can override these defaults using
DestinationRuleresources. For example, to increase the maximum connections per host and per outbound cluster:apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: details-dr spec: host: details trafficPolicy: connectionPool: tcp: maxConnections: 1000 # Increased from default of 100 http: http1MaxPendingRequests: 100 # Increased from default of 10 maxRequestsPerConnection: 100 # Increased from default of 1Increasing
maxConnectionsandmaxRequestsPerConnectionallows Envoy to handle more concurrent requests to a single upstream service instance without establishing new connections for every request, reducing connection setup overhead and improving throughput. -
ISTIO_META_RBAC_ENABLEDEnvironment Variable: In youristio-proxycontainer’s environment variables within theIstioOperatoror pod template, you can setISTIO_META_RBAC_ENABLEDtofalseif you are not using Istio’s RBAC (AuthorizationPolicy) features.apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: values: global: proxy: env: - name: ISTIO_META_RBAC_ENABLED value: "false"Disabling RBAC means Envoy doesn’t perform authorization checks for every request, reducing latency and CPU usage for requests that would otherwise pass authorization.
-
ISTIO_META_TLS_MODEEnvironment Variable: If you are not using mTLS for all services (e.g., using permissive mode or disabling mTLS where it’s not strictly necessary), ensureISTIO_META_TLS_MODEis set appropriately. For permissive mode, it’sPERMISSIVE. If you’ve selectively disabled mTLS, you might need to tune specificDestinationRuletlssettings. However, if you’ve disabled it entirely for a specific service (e.g.,tls.mode: ISTIO_MUTUALis removed fromDestinationRule), the sidecar might still perform some TLS handshake overhead checks. Ensuring it’s truly off where not needed is key.You can control this at the pod level via annotations, or more globally via
IstioOperator:apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: values: global: meshConfig: defaultConfig: proxyMetadata: ISTIO_META_TLS_MODE: "PERMISSIVE" # Or NONE if you've disabled it everywhereSetting
ISTIO_META_TLS_MODEtoPERMISSIVE(orNONEif you have explicitly disabled mTLS inDestinationRules or viameshConfig) prevents Envoy from attempting or enforcing mTLS handshakes when they are not required, directly reducing latency and CPU load associated with TLS operations.
The most subtle performance impact comes from the default behavior of Envoy’s connection pooling for HTTP/1.1. By default, Envoy often creates a new connection for each request to an upstream host (maxRequestsPerConnection: 1). This is a conservative setting to prevent a single lingering connection from blocking a thread, but for high-throughput scenarios, it means a massive amount of connection setup and teardown overhead. You’ll see this reflected in high http1_cx_total and http1_cx_destroy metrics in Envoy’s stats.
If you fix all of the above, you’ll likely start seeing issues with OutOfMemory errors on your application pods as the sidecar’s resource limits become more constrictive.