Overview
Istioβs traffic management is built on custom resources that map directly to Envoy configuration. The control plane (istiod) watches these CRDs, translates them into Envoy-native xDS configuration, and pushes updates to every sidecar proxy in the mesh via gRPC streams.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ISTIO TRAFFIC MANAGEMENT STACK β
β β
β User defines CRDs istiod translates Envoy applies β
β ββββββββββββββββββ ββββββββββββββββββ βββββββββββββ β
β β
β VirtualService ββββββββββΊ RDS (Route Discovery) βββββΊ Route rules β
β DestinationRule ββββββββββΊ CDS (Cluster Discovery)βββββΊ LB / CB / OD β
β Gateway ββββββββββΊ LDS (Listener Discovery)βββΊ Listeners β
β ServiceEntry ββββββββββΊ CDS + EDS βββββΊ External hosts β
β Sidecar ββββββββββΊ LDS + CDS scope βββββΊ Proxy config β
β β
β K8s Services ββββββββββΊ EDS (Endpoint Discovery)βββΊ Pod IPs β
β K8s Endpoints ββββββββββΊ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
For the Istio control plane architecture, xDS protocol, and request lifecycle, see Istio Architecture Deep Dive. For Envoy internals (filter chains, connection pooling), see Istio Envoy Internals.
Sidecar Traffic Interception: istio-init and iptables
Istio intercepts all pod traffic by injecting an init container (istio-init) that sets up iptables REDIRECT rules in the podβs network namespace (not the nodeβs). These rules redirect traffic to the Envoy sidecar before it reaches the app or leaves the pod.
Where the iptables rules live
iptables rules are per network namespace. Each pod gets its own namespace, its own rules. The nodeβs root namespace (kube-proxy rules) is untouched.
istio-init lifecycle
istio-init is a Kubernetes init container β runs once, installs iptables rules, exits. The rules persist in the namespace after the container dies (rules belong to the namespace, not the process). Requires NET_ADMIN capability. Alternative: Istio CNI plugin does this at the CNI level, removing the NET_ADMIN requirement.
Core iptables rules
# Inbound: external traffic β redirect to Envoy inbound listener
-A PREROUTING -p tcp --dport <app-port> -j REDIRECT --to-port 15006
# Outbound: app traffic β redirect to Envoy outbound listener
-A OUTPUT -p tcp -j REDIRECT --to-port 15001
# Prevent infinite loop: Envoy (UID 1337) traffic skips redirect
-A OUTPUT -m owner --uid-owner 1337 -j RETURN
Traffic flow through netfilter chains
The Linux kernel has one netfilter pipeline per network namespace. Which chain a packet hits depends on its origin, not the interface:
INCOMING (from outside pod) LOCALLY GENERATED (by a process)
β β
βΌ β
PREROUTING β
β β
βΌ β
routing decision βββββββββββββββββββββββ OUTPUT
β± β² β²
β± β² β
for this forward to local process
host? another host? generates packet
β β
βΌ βΌ
INPUT FORWARD
β β
βΌ βΌ
local process POSTROUTING β out
Key insight: Locally generated packets (e.g., Envoy β
127.0.0.1:8080) enter at OUTPUT, never PREROUTING. PREROUTING only hooks packets arriving from a network interface. This is fundamental netfilter design, not a special case. (ref: nftables wiki β Netfilter hooks)
Outbound: App β External Service
App sends to 10.0.5.3:443
β OUTPUT chain β matches REDIRECT rule β rewritten to 127.0.0.1:15001
β Envoy receives on :15001, recovers original dst via SO_ORIGINAL_DST
β Envoy applies routing/mTLS/retries, opens new connection to 10.0.5.3:443
β OUTPUT chain again β UID=1337 β RETURN (skip redirect)
β packet leaves pod via eth0 β CNI β destination
Inbound: External β App
Traffic arrives at pod:8080
β PREROUTING chain β REDIRECT to 127.0.0.1:15006
β Envoy receives on :15006, applies mTLS termination/authz/telemetry
β Envoy forwards to 127.0.0.1:8080
β OUTPUT chain β UID=1337 β RETURN (skip redirect)
β app receives on :8080 (no PREROUTING β it's locally generated loopback)
Why Envoy β app loopback isnβt re-intercepted
Envoyβs packet to 127.0.0.1:8080 is locally generated β it goes through OUTPUT (where UID 1337 matches RETURN), not PREROUTING. Loopback delivery follows: OUTPUT β routing β POSTROUTING β INPUT β app. PREROUTING is never involved because the packet never enters from an external interface.
VirtualService β Envoy Route Configuration (RDS)
A VirtualService defines how requests to a hostname are routed to actual service backends. It decouples the destination the client addresses from the actual workload that handles the request, enabling canary deployments, A/B testing, and header-based routing without changing application code.
Hosts Field
The hosts field specifies which destination hostnames this VirtualService applies to. These can be:
- Kubernetes short names (
reviews) β resolved toreviews.{namespace}.svc.cluster.local - FQDNs (
reviews.default.svc.cluster.local) - Wildcard prefixes (
*.example.com) β matches any subdomain - IP addresses (for TCP routing)
A requestβs Host header (HTTP) or SNI (TLS) is matched against these values.
Match Conditions: AND vs OR Logic
Routing rules are evaluated top-to-bottom β the first match wins. Within a single rule, match conditions follow specific AND/OR semantics:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MATCH CONDITION LOGIC β
β β
β http: β
β - match: β
β - headers: β β
β end-user: β All conditions within β
β exact: jason ββ a SINGLE match block β
β uri: β are ANDed together β
β prefix: /api β β
β - headers: β β
β end-user: ββ Multiple match blocks β
β exact: admin β are ORed β
β route: ... β
β β
β Evaluates as: β
β (header=jason AND uri=/api*) OR (header=admin) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Match fields available: uri (exact/prefix/regex), headers, queryParams, method, port, sourceLabels, sourceNamespace, gateways.
Full Example with Envoy Translation
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews-route
spec:
hosts:
- reviews # β Envoy virtual host match
http:
- match:
- headers:
end-user:
exact: jason # β Route match condition
route:
- destination:
host: reviews # β Envoy cluster
subset: v2 # β From DestinationRule
weight: 100
- route: # β Default route (no match = catch-all)
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v3
weight: 10
retries:
attempts: 3 # β Envoy retry policy
perTryTimeout: 2s
timeout: 10s # β Envoy route timeoutThis translates to Envoy config:
Envoy Route Configuration (RDS):
virtual_host: "reviews.default.svc.cluster.local:8080"
routes:
- match: { headers: [{ name: "end-user", exact_match: "jason" }] }
route: { cluster: "outbound|8080|v2|reviews.default.svc.cluster.local" }
- match: { prefix: "/" }
route:
weighted_clusters:
clusters:
- { name: "outbound|8080|v1|reviews.default...", weight: 90 }
- { name: "outbound|8080|v3|reviews.default...", weight: 10 }
retry_policy: { num_retries: 3, per_try_timeout: "2s" }
timeout: "10s"
Gotcha: Always include a default route (no
matchcondition) as the last rule. Without it, requests that donβt match any condition get a 404 from Envoy. Istio does NOT fall back to Kubernetes default routing when a VirtualService exists for a host β the VirtualService takes full ownership of that hostβs routing.
DestinationRule β Envoy Cluster Configuration (CDS)
DestinationRules define how traffic reaches a destination after routing. They configure the Envoy cluster: load balancing algorithm, connection pool limits, circuit breaker thresholds, outlier detection (passive health checking), TLS settings, and service subsets.
DestinationRules apply after VirtualService routing decisions. A VirtualService says βsend to cluster X, subset Yβ β the DestinationRule says βhereβs how to talk to subset Y.β
Subsets
Subsets partition a serviceβs endpoints by label selector (typically version). Each subset becomes a separate Envoy cluster with its own endpoint list (via EDS). Subset-level traffic policies override the top-level trafficPolicy.
Load Balancing Algorithms
| Algorithm | Envoy lb_policy | Behavior |
|---|---|---|
LEAST_REQUEST | LEAST_REQUEST | Default. Picks two random endpoints, sends to the one with fewer active requests. Good general-purpose choice. |
ROUND_ROBIN | ROUND_ROBIN | Sequential rotation through endpoints. Simple, predictable. |
RANDOM | RANDOM | Uniform random selection. Performs well under high concurrency. |
PASSTHROUGH | CLUSTER_PROVIDED | Sends to the caller-specified address directly. Used for ServiceEntry with static IPs. |
Consistent hash load balancing enables session affinity β the same client always reaches the same endpoint (until endpoint membership changes):
trafficPolicy:
loadBalancer:
consistentHashLB:
httpHeaderName: x-user-id # hash by header value
# OR: httpCookie, useSourceIp, httpQueryParameterName
minimumRingSize: 1024Hash key options: HTTP header, HTTP cookie (Envoy generates a cookie if missing), source IP, or query parameter. Under the hood, this maps to Envoyβs ring hash or Maglev algorithm.
Full Example
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews-destination
spec:
host: reviews
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100 # β Envoy circuit breaker
http:
h2UpgradePolicy: DEFAULT
maxRequestsPerConnection: 1
outlierDetection: # β Envoy outlier detection
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
loadBalancer:
simple: ROUND_ROBIN # β Envoy LB policy
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3This creates three Envoy clusters:
Cluster: "outbound|8080|v1|reviews.default.svc.cluster.local"
- lb_policy: ROUND_ROBIN
- circuit_breakers: { max_connections: 100 }
- outlier_detection: { consecutive_5xx: 5, interval: 30s }
- endpoints (from EDS): [pods with label version=v1]
Cluster: "outbound|8080|v2|reviews.default.svc.cluster.local"
- (same policies)
- endpoints: [pods with label version=v2]
Cluster: "outbound|8080|v3|reviews.default.svc.cluster.local"
- (same policies)
- endpoints: [pods with label version=v3]
Traffic Splitting, Retries, Timeouts, Circuit Breaking
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β How They Map to Envoy β
β β
β VirtualService Envoy Concept β
β βββββββββββββ ββββββββββββββ β
β http[].route[].weight β weighted_clusters β
β http[].retries β retry_policy on route β
β http[].timeout β timeout on route β
β http[].fault β fault injection filter β
β http[].mirror β request_mirror_policy β
β β
β DestinationRule Envoy Concept β
β βββββββββββββββ ββββββββββββββ β
β trafficPolicy.connectionPool β circuit_breakers thresholds β
β trafficPolicy.outlierDetection β outlier_detection β
β trafficPolicy.loadBalancer β lb_policy on cluster β
β trafficPolicy.tls β transport_socket (upstream TLS)β
β subsets[] β separate cluster per subset β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Ingress: Istio Gateway and Kubernetes Gateway API
North-south traffic (from external clients into the mesh) requires a dedicated ingress point. Istio has historically used its own Gateway CRD, and now fully supports the Kubernetes Gateway API as the recommended approach.
Istio Gateway CRD (Legacy / networking.istio.io)
The Istio Gateway resource configures an Envoy proxy deployed as a standalone Deployment + Service (typically istio-ingressgateway) at the edge of the mesh. It defines which ports, protocols, and TLS settings the gateway should expose. A VirtualService is then bound to the Gateway to define routing rules.
ββββββββββββββββββββββββββββββββββββββββ
External β K8s Cluster β
Client β β
β β βββββββββββββββββββββββ β
β HTTPS :443 β β istio-ingressgateway β β
ββββββββββββββββββββββββββββββββΊβ β (Envoy proxy) β β
β β β β β
β β β Gateway: ports, β β
β β β TLS termination β β
β β β VirtualService: β β
β β β route to backends β β
β β ββββββββββββ¬ββββββββββββ β
β β β β
β β βββββββββ΄βββββββββ β
β β βΌ βΌ β
β β ββββββββ ββββββββ β
β β βPod A β βPod B β β
β β β+proxyβ β+proxyβ β
β β ββββββββ ββββββββ β
β ββββββββββββββββββββββββββββββββββββββββ
# 1. Gateway: configures the ingress listener
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: my-gateway
spec:
selector:
istio: ingressgateway # β selects the gateway deployment
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE # β TLS termination at the gateway
credentialName: my-tls-cert # β K8s Secret with cert/key
hosts:
- "api.example.com" # β SNI / Host match
# 2. VirtualService: binds to the Gateway for routing
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: api-routes
spec:
hosts:
- "api.example.com"
gateways:
- my-gateway # β binds to the Gateway above
http:
- match:
- uri:
prefix: /v1
route:
- destination:
host: api-v1
port:
number: 8080
- match:
- uri:
prefix: /v2
route:
- destination:
host: api-v2
port:
number: 8080This translates to Envoy config on the istio-ingressgateway pod:
LDS: listener on 0.0.0.0:443
β filter_chain:
transport_socket: TLS (cert from SDS, credentialName: "my-tls-cert")
filters:
β http_connection_manager
β RDS route_config:
virtual_host: "api.example.com"
routes:
- match: { prefix: "/v1" }
route: { cluster: "outbound|8080||api-v1.default.svc.cluster.local" }
- match: { prefix: "/v2" }
route: { cluster: "outbound|8080||api-v2.default.svc.cluster.local" }
Key limitation of the Istio Gateway CRD: The Gateway and VirtualService are separate resources that reference each other by name. There is no ownership relationship β a misconfigured VirtualService can silently fail to bind. The role of infrastructure admin (who manages the gateway) and application developer (who manages routes) are not clearly separated.
Kubernetes Gateway API (Current Standard)
The Kubernetes Gateway API (gateway.networking.k8s.io) is a SIG-Network project that provides a standard, portable API for ingress across mesh implementations. Istio adopted it as the recommended API starting from Istio 1.16 and it reached GA in Istio 1.22+. It replaces both the Istio Gateway CRD and Ingress resource.
The Gateway API introduces a clean role-based resource model:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β KUBERNETES GATEWAY API RESOURCE MODEL β
β β
β Infrastructure Provider Cluster Operator App Developer β
β ββββββββββββββββββββ βββββββββββββββ ββββββββββββββ β
β β
β ββββββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β GatewayClass βββββββββ β Gateway ββββββββ β HTTPRoute β β
β β β refs β β refs β (or GRPCRouteβ β
β β - controller name β β - listeners β β TCPRoute, β β
β β (istio.io/ β β - ports, TLS β β TLSRoute) β β
β β gateway- β β - addresses β β β β
β β controller) β β - allowed β β - hostnames β β
β β β β routes β β - rules β β
β ββββββββββββββββββββ ββββββββββββββββ β - backends β β
β ββββββββββββββββ β
β β
β "Who provides the "Which ports/protocols "Where does traffic β
β infrastructure?" does this gateway expose?" for my app go?" β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Resource | Who Manages | Purpose | Analogous Istio CRD |
|---|---|---|---|
| GatewayClass | Infrastructure provider | Defines the controller implementation (e.g. istio.io/gateway-controller) | N/A (implicit) |
| Gateway | Cluster operator / platform team | Configures listeners (ports, TLS, allowed routes) | Gateway (networking.istio.io) |
| HTTPRoute | Application developer | Defines routing rules, backends, filters | VirtualService |
| GRPCRoute | Application developer | gRPC-specific routing rules | VirtualService (with gRPC match) |
| TCPRoute / TLSRoute | Application developer | L4 routing | VirtualService (with TCP/TLS match) |
| ReferenceGrant | Namespace owner | Allows cross-namespace references | N/A |
# 1. GatewayClass -- typically created once by the platform team
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: istio
spec:
controllerName: istio.io/gateway-controller
# 2. Gateway -- creates an Envoy deployment + service automatically
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
namespace: istio-ingress
spec:
gatewayClassName: istio # β references the GatewayClass
listeners:
- name: https
port: 443
protocol: HTTPS
hostname: "api.example.com"
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: api-tls-cert
allowedRoutes:
namespaces:
from: Selector # β only specific namespaces can attach
selector:
matchLabels:
shared-gateway: "true"
# 3. HTTPRoute -- created by app developers in their own namespace
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-routes
namespace: my-app # β different namespace from Gateway
spec:
parentRefs:
- name: my-gateway
namespace: istio-ingress # β attaches to the Gateway
hostnames:
- "api.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /v1
backendRefs:
- name: api-v1
port: 8080
weight: 90
- name: api-v2
port: 8080
weight: 10 # β traffic splitting built in
- matches:
- path:
type: PathPrefix
value: /healthz
filters:
- type: RequestHeaderModifier # β header manipulation
requestHeaderModifier:
add:
- name: X-Health-Check
value: "true"
backendRefs:
- name: health-svc
port: 8080How Istio implements the Gateway API:
When you create a Gateway resource referencing a GatewayClass with controllerName: istio.io/gateway-controller, Istio automatically provisions:
- An Envoy
Deployment(the gateway proxy) - A
Service(typically LoadBalancer) to expose it - A
ServiceAccountwith the appropriate RBAC
This is a major difference from the Istio Gateway CRD, where you had to manually deploy istio-ingressgateway. With the Gateway API, the lifecycle is fully automated β delete the Gateway resource and the deployment is cleaned up.
Gateway API resource created
β
βΌ
istiod watches gateway.networking.k8s.io/v1
β
ββββΊ Creates Deployment (Envoy proxy) with matching labels
ββββΊ Creates Service (LoadBalancer / ClusterIP)
ββββΊ Creates ServiceAccount + RBAC
β
βΌ
istiod translates Gateway listeners + HTTPRoute rules
into xDS config and pushes to the new Envoy proxy
β
βΌ
Envoy proxy starts serving traffic with
routes defined by HTTPRoute resources
Istio Gateway CRD vs Kubernetes Gateway API
| Aspect | Istio Gateway CRD | Kubernetes Gateway API |
|---|---|---|
| API group | networking.istio.io/v1 | gateway.networking.k8s.io/v1 |
| Status | Supported (not deprecated) | Recommended (Istio 1.22+) |
| Gateway provisioning | Manual (deploy istio-ingressgateway) | Automatic (Istio creates Deployment + Service) |
| Route binding | VirtualService gateways field (by name) | HTTPRoute parentRefs (explicit, with status feedback) |
| Cross-namespace | Allowed implicitly | Requires explicit allowedRoutes + ReferenceGrant |
| Role separation | Weak (same team manages Gateway + VS) | Strong (GatewayClass / Gateway / Route layers) |
| Status reporting | Limited | Rich (route conditions, accepted/rejected per parent) |
| Portability | Istio-specific | Portable across implementations (Istio, Envoy Gateway, Cilium, etc.) |
| Mesh (east-west) | VirtualService for internal routing | HTTPRoute can also be used for mesh routing (experimental in some versions) |
Note: The Istio Gateway CRD is not deprecated as of Istio 1.24. Both APIs work side by side. However, all new Istio documentation and examples default to the Gateway API, and the community direction is clearly toward it. For new deployments, use the Kubernetes Gateway API.
Waypoint Proxies and the Gateway API
In Ambient mode, waypoint proxies are also managed via the Gateway API:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-ns-waypoint
namespace: my-namespace
labels:
istio.io/waypoint-for: service # β "service" or "workload"
spec:
gatewayClassName: istio-waypoint # β special class for waypoints
listeners:
- name: mesh
port: 15008 # β HBONE port
protocol: HBONEThis creates a waypoint proxy Deployment in the namespace. All L7 policies (HTTPRoute, AuthorizationPolicy with HTTP conditions) are enforced at this waypoint. The Gateway API unifies both ingress and mesh-internal L7 proxies under a single API surface.
ServiceEntry: Adding External Services to the Mesh
By default, Istio-proxied services can reach external hosts, but traffic to those hosts bypasses Istioβs routing, resilience, and observability features. A ServiceEntry registers an external service into Istioβs internal service registry so that Envoy can apply the full traffic management stack (retries, timeouts, circuit breaking, mTLS) to external traffic.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WITHOUT ServiceEntry β
β β
β Pod βββΊ Envoy sidecar βββΊ ext-api.example.com β
β β β
β ββ passthrough cluster (no Istio policies) β
β no retries, no timeouts, no metrics β
β β
β WITH ServiceEntry β
β β
β Pod βββΊ Envoy sidecar βββΊ ext-api.example.com β
β β β
β ββ named cluster with full Envoy config β
β retries, timeouts, circuit breaking, metrics β
β VirtualService + DestinationRule can attach β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: external-api
spec:
hosts:
- ext-api.example.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL # outside the mesh (vs MESH_INTERNAL)
resolution: DNS # resolve via DNS (vs STATIC, NONE)location values:
MESH_EXTERNALβ external service, mTLS not applied to outbound connectionMESH_INTERNALβ treat as a mesh service (e.g., a VM running an Envoy sidecar registered to the mesh)
resolution values:
DNSβ Envoy resolves the hostname via DNS and load-balances across returned IPsSTATICβ use explicitly listedendpointswith IP addressesNONEβ forward to the IP the caller connected to (used for wildcard hosts)
Once registered, you can apply VirtualService and DestinationRule to the ServiceEntry host just like any in-mesh service:
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: external-api-route
spec:
hosts:
- ext-api.example.com
http:
- timeout: 5s
retries:
attempts: 2
perTryTimeout: 2s
route:
- destination:
host: ext-api.example.com
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: external-api-dr
spec:
host: ext-api.example.com
trafficPolicy:
tls:
mode: SIMPLE # originate TLS to the external service
connectionPool:
tcp:
maxConnections: 50
outlierDetection:
consecutive5xxErrors: 3
interval: 10s
baseEjectionTime: 30sSidecar Resource: Limiting Proxy Scope
By default, every Envoy sidecar is configured with routes and clusters for every service in the mesh. In large meshes (hundreds or thousands of services), this means every proxy holds a large xDS configuration, consuming significant memory and increasing config push times.
The Sidecar resource limits which services a proxy can reach (egress) and which ports it accepts traffic on (ingress), reducing the xDS config footprint.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DEFAULT (no Sidecar resource) β
β β
β Envoy sidecar config includes: β
β - Clusters for ALL services across ALL namespaces β
β - Routes for ALL VirtualServices β
β - Memory: potentially hundreds of MB in large meshes β
β β
β WITH Sidecar resource β
β β
β Envoy sidecar config includes: β
β - Only clusters for services in specified namespaces/hosts β
β - Dramatically smaller config, faster xDS pushes β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
apiVersion: networking.istio.io/v1
kind: Sidecar
metadata:
name: default
namespace: bookinfo
spec:
workloadSelector: # omit to apply to ALL pods in namespace
labels:
app: reviews
egress:
- hosts:
- "./*" # all services in same namespace
- "istio-system/*" # istiod, telemetry, etc.
- "external-ns/ext-api.example.com" # specific external service
ingress:
- port:
number: 8080
protocol: HTTP
name: http
defaultEndpoint: 127.0.0.1:8080 # forward to app containerScope rules:
- A namespace-wide Sidecar (no
workloadSelector) applies to all workloads in that namespace unless a more specific workload-scoped Sidecar exists - Only one namespace-wide Sidecar allowed per namespace
- The
egress.hostsfield usesnamespace/hostformat..means the Sidecarβs own namespace - Omitting the egress section means the proxy can reach everything (default behavior)
Best practice for large meshes: Define a namespace-wide Sidecar in each namespace restricting egress to only the services that namespace actually calls. This can reduce Envoy memory usage by 10x or more.
Egress Gateways: Controlled External Access
An egress gateway is a dedicated Envoy proxy at the mesh edge that handles outbound traffic to external services. It provides a centralized point for monitoring, controlling, and securing all traffic leaving the mesh.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β K8s Cluster β
β β
β ββββββββββββ ββββββββββββββββββββ ββββββββββββββββββββββ β
β β Pod β β istio-egress- β β External Service β β
β β (+ sidecar)βββββΊβ gateway ββββββΊβ ext-api.example β β
β ββββββββββββ β (Envoy proxy) β β .com β β
β β β ββββββββββββββββββββββ β
β β - TLS originationβ β
β β - Access logging β β
β β - Policy enforce β β
β ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Use cases:
- Security policy: Force all external traffic through a single exit point for auditing and firewall rules
- TLS origination: Internal services send plain HTTP; the egress gateway upgrades to HTTPS
- Network topology: Nodes without direct internet access route through the egress gateway
# 1. ServiceEntry for external host
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: ext-svc
spec:
hosts:
- ext-api.example.com
ports:
- number: 443
name: tls
protocol: TLS
resolution: DNS
location: MESH_EXTERNAL
# 2. Gateway for egress
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: egress-gateway
spec:
selector:
istio: egressgateway
servers:
- port:
number: 443
name: tls
protocol: TLS
hosts:
- ext-api.example.com
tls:
mode: PASSTHROUGH # pass TLS through without terminating
# 3. VirtualService: route mesh traffic β egress gateway β external
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ext-via-egress
spec:
hosts:
- ext-api.example.com
gateways:
- mesh # applies to sidecar-to-egress routing
- egress-gateway # applies at the egress gateway itself
tls:
- match:
- gateways: [mesh]
port: 443
sniHosts: [ext-api.example.com]
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
port:
number: 443
- match:
- gateways: [egress-gateway]
port: 443
sniHosts: [ext-api.example.com]
route:
- destination:
host: ext-api.example.com
port:
number: 443Network Resilience: Timeouts, Retries, Circuit Breaking, Fault Injection
These features make services resilient to failures without requiring application code changes. They are configured via VirtualService (timeouts, retries, fault injection) and DestinationRule (circuit breaking, outlier detection).
Timeouts
http:
- route:
- destination:
host: ratings
timeout: 10s # total request timeoutIstioβs default: no timeout (disabled). When set, Envoy returns 504 Gateway Timeout if the upstream does not respond within the specified duration. The timeout covers the entire request, including all retries.
Gotcha: If the application also sets a timeout (e.g., HTTP client timeout), the shorter of the two wins. A common pitfall: application timeout is 3s, Istio timeout is 10s with 3 retries β the application gives up before retries complete. Always coordinate timeouts across layers.
Retries
http:
- route:
- destination:
host: ratings
retries:
attempts: 3 # max retry attempts (total calls = attempts + 1... NO)
perTryTimeout: 2s # timeout per individual attempt
retryOn: 5xx,reset,connect-failure,retriable-4xxNote: The
attemptsfield specifies the total number of attempts including the first try, not retries after the first try. Soattempts: 3means up to 3 total calls (1 original + 2 retries). This matches Envoyβsnum_retriesbehavior wherenum_retries: 3means 3 retries after the first attempt β but Istioβsattemptsis mapped asnum_retries = attempts - 1in Envoy config, soattempts: 3=num_retries: 2.
Istio default: 2 attempts, with retry conditions connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes. Retries are automatically spaced with a 25ms+ base interval with random jitter to avoid thundering-herd on a failing service.
retryOn values (maps to Envoyβs retry_on):
| Value | Retries when⦠|
|---|---|
5xx | Upstream returns any 5xx status |
gateway-error | 502, 503, or 504 |
reset | Upstream resets the connection (TCP RST) |
connect-failure | Connection to upstream fails entirely |
retriable-4xx | Upstream returns a retriable 4xx (409 Conflict) |
refused-stream | Upstream sends REFUSED_STREAM (HTTP/2) |
retriable-status-codes | Upstream returns a status in retriableStatusCodes list |
Circuit Breaking (via DestinationRule)
Circuit breaking prevents cascading failures by capping concurrent connections, requests, and retries to an upstream cluster. When thresholds are exceeded, Envoy immediately returns 503 with response flag UO (upstream overflow) instead of queueing.
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews-cb
spec:
host: reviews
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100 # max TCP connections to upstream
http:
h2UpgradePolicy: DEFAULT
http1MaxPendingRequests: 100 # max queued requests when all conns busy
http2MaxRequests: 1000 # max concurrent HTTP/2 requests
maxRequestsPerConnection: 10 # close conn after N requests (prevents stale)
maxRetries: 3 # max concurrent retries to upstream
outlierDetection:
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CIRCUIT BREAKER FLOW β
β β
β New request arrives at Envoy β
β β β
β βΌ β
β Check: active connections < maxConnections? β
β βββ No β 503 (UO flag) immediately β
β βββ Yes β
β β β
β βΌ β
β Check: pending requests < http1MaxPendingRequests? β
β βββ No β 503 (UO flag) immediately β
β βββ Yes β
β β β
β βΌ β
β Forward request to upstream β
β β β
β βΌ β
β Response: 5xx? β
β βββ Yes β increment outlier counter for that endpoint β
β β consecutive5xxErrors >= threshold? β
β β βββ Yes β EJECT endpoint for baseEjectionTime β
β βββ No β reset consecutive error counter β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Fault Injection
Fault injection tests resilience by injecting delays or HTTP errors at the Envoy proxy layer (L7), not at the network layer. This tests actual application behavior including timeouts, retry logic, and fallback mechanisms.
Two types:
- Delays β adds latency before forwarding the request
- Aborts β returns an error code without forwarding
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings-fault
spec:
hosts:
- ratings
http:
- fault:
delay:
percentage:
value: 10 # 10% of requests
fixedDelay: 5s # add 5s delay
abort:
percentage:
value: 0.1 # 0.1% of requests
httpStatus: 500 # return 500 error
route:
- destination:
host: ratings
subset: v1Fault injection happens before the request reaches the upstream, so it tests the calling serviceβs handling of slow/failed dependencies. The delay is applied before forwarding, and abort replaces the upstream response entirely.
Important: Fault injection and retry/timeout config on the same VirtualService route can interact unexpectedly. For example, injecting a 5s delay with a 3s timeout on the same route means the delayed requests will always timeout. Test fault injection on the destination serviceβs VirtualService, not the callerβs.
See also
- Istio Architecture Deep Dive β control plane, xDS, sidecar injection, iptables, request lifecycle
- Istio Envoy Internals β Envoy proxy pipeline, threading, filters, connection pooling
- Istio Security β mTLS, AuthorizationPolicy, RequestAuthentication, ext_authz
- Istio Observability & Extensibility β metrics, tracing, WasmPlugin, EnvoyFilter
- Istio Traffic Management Concepts
- Istio VirtualService Reference
- Istio DestinationRule Reference
- Istio ServiceEntry Reference
- Istio Sidecar Reference
- Envoy Circuit Breaking
- Kubernetes Gateway API
Interview Prep
Q: Walk through how a VirtualService and DestinationRule work together to implement a canary deployment.
A: A canary deployment gradually shifts traffic from an old version to a new one. Here is the end-to-end flow:
1. DestinationRule defines subsets (v1, v2) based on pod labels
2. VirtualService splits traffic: 90% β v1, 10% β v2
Client request
β
βΌ
Envoy sidecar (caller)
β
βββ RDS: route config from VirtualService
β weighted_clusters: v1=90, v2=10
β
βββ 90% chance β CDS cluster: outbound|8080|v1|reviews...
β βββ EDS: pods with version=v1
β
βββ 10% chance β CDS cluster: outbound|8080|v2|reviews...
βββ EDS: pods with version=v2
The VirtualService handles route-level weight splitting. The DestinationRule maps subset names to label selectors and configures per-subset policies (e.g., different circuit breaker thresholds for the canary). You gradually increase the v2 weight (10 β 25 β 50 β 100) as confidence grows.
Key point: Without a DestinationRule defining subsets, you cannot reference subset: v2 in the VirtualService β Envoy wonβt have a cluster for it.
Q: What is the Kubernetes Gateway API and how does Istio use it? How does it differ from the Istio Gateway CRD?
A: The Kubernetes Gateway API (gateway.networking.k8s.io) is a SIG-Network standard that provides a role-oriented, portable API for managing ingress and mesh traffic. It has three core resources:
- GatewayClass: Defines the controller implementation (e.g.,
istio.io/gateway-controller). Managed by infrastructure providers. - Gateway: Configures listeners (ports, TLS, hostname matching, allowed routes). Managed by cluster operators.
- HTTPRoute (also GRPCRoute, TCPRoute, TLSRoute): Defines routing rules and backends. Managed by application developers.
Key differences from the Istio Gateway CRD:
- Automated provisioning: When you create a Gateway API
Gatewayresource, Istio automatically creates the Envoy Deployment, Service, and ServiceAccount. With the Istio CRD, you had to manually deployistio-ingressgateway. - Role separation: The three-tier model (GatewayClass β Gateway β Route) cleanly separates infrastructure, platform, and application concerns. The Istio CRD mixed these β the same team often managed both Gateway and VirtualService.
- Cross-namespace safety: The Gateway API requires explicit
allowedRouteson the Gateway andReferenceGrantfor cross-namespace references. The Istio CRD allowed implicit cross-namespace binding. - Status feedback: HTTPRoute reports rich status conditions (Accepted, ResolvedRefs) per parent Gateway. The Istio VirtualService had limited status reporting.
- Portability: Gateway API works across Istio, Envoy Gateway, Cilium, and other implementations. The Istio CRD is Istio-specific.
Istio adopted the Gateway API as the recommended API starting with 1.16, reaching GA in 1.22+. The Istio CRD is not deprecated but all new documentation defaults to the Gateway API.
In Ambient mode, waypoint proxies are also managed via Gateway API using gatewayClassName: istio-waypoint, unifying both ingress and mesh-internal L7 proxies under one API.
Q: What is the difference between circuit breaking and outlier detection in Istio?
A: Both are configured via DestinationRule but serve different purposes:
βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β Circuit Breaking β Outlier Detection β
β (connectionPool) β (outlierDetection) β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β Prevents overloading β Removes unhealthy endpoints β
β upstream with too many β from the load balancer pool β
β concurrent requests β based on observed errors β
β β β
β Thresholds: β Thresholds: β
β - maxConnections β - consecutive5xxErrors β
β - http1MaxPendingReqs β - consecutiveGatewayErrors β
β - http2MaxRequests β - interval (check window) β
β - maxRetries β - baseEjectionTime β
β β - maxEjectionPercent β
β When exceeded: β When exceeded: β
β β 503 immediately β β endpoint ejected for a duration β
β (response flag: UO) β (exponential backoff on repeat) β
β β β
β Scope: entire cluster β Scope: individual endpoint β
βββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
Circuit breaking protects the upstream service from being overwhelmed. Outlier detection protects the caller from wasting requests on a broken endpoint. They complement each other β circuit breaking caps total load, outlier detection removes the bad pods from rotation.
Q: What happens when a VirtualService exists for a host but the request doesnβt match any route rule?
A: The request gets a 404 Not Found from Envoy. Once a VirtualService claims a host (via the hosts field), it takes full ownership of that hostβs routing in Envoyβs RDS config. Envoy does NOT fall back to Kubernetes default round-robin routing. This is why you should always include a catch-all route (no match condition) as the last rule.
Q: Why would you use a ServiceEntry? What can you do with it that you cannot do without it?
A: Without a ServiceEntry, traffic to external hosts goes through Envoyβs passthrough cluster β it reaches the destination, but Istio cannot apply retries, timeouts, circuit breaking, mTLS, or traffic shifting. You also get no telemetry (no metrics, no access logs with proper service names).
With a ServiceEntry:
- Apply VirtualService routing rules (timeouts, retries, fault injection, traffic splitting)
- Apply DestinationRule policies (circuit breaking, outlier detection, TLS origination)
- Get full Envoy metrics and access logs for external traffic
- Use egress gateways to funnel all external traffic through a controlled exit point
Q: How does the Sidecar resource improve performance in large meshes?
A: By default, every Envoy sidecar receives xDS configuration for every service in the mesh. In a mesh with 500 services, each sidecar holds 500+ cluster definitions, route tables, and endpoint lists. This causes:
- High memory usage per proxy (can exceed 100MB)
- Slow xDS push times (istiod must push to every proxy on any service change)
- Longer Envoy startup times
The Sidecar resourceβs egress.hosts field restricts which services a proxy knows about. A workload that only calls 5 services only gets xDS config for those 5 services. This reduces memory 10-50x and makes xDS pushes incremental and targeted.
Q: Explain the interaction between Istio retries and timeouts. What is perTryTimeout vs the overall timeout?
A:
βββββββββββββββββββββββββββββββββββββ timeout: 10s βββββββββββββββββββββββ
β β
β βββ perTryTimeout: 3s βββ βββ perTryTimeout: 3s βββ βββ 3s βββ β
β β Attempt 1 β β Attempt 2 (retry) β β Att 3 β β
β β (connect + wait β β (connect + wait β β (retry) β β
β β for response) β β for response) β β β β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ βββββββββββ β
β β
β t=0 t=3s t=6s t=10s β
β start retry 1 retry 2 give β
β (attempt 1 timed out) starts up β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
timeoutis the total time allowed for the entire request including all retries. Default: disabled (no timeout).perTryTimeoutis the max time for each individual attempt. Default: same as the overall route timeout.attempts: 3means up to 3 total calls. In Envoy terms,num_retries= attempts - 1 = 2 retries.
If the overall timeout expires mid-retry, Envoy stops retrying and returns 504. If perTryTimeout expires on one attempt, Envoy starts the next retry (if attempts remain and overall timeout hasnβt expired).
Q: How does consistent hash load balancing work in Istio? When would you use it?
A: Consistent hash LB ensures the same client always reaches the same backend endpoint, enabling session affinity without server-side session stores. Configured in DestinationRule:
trafficPolicy:
loadBalancer:
consistentHashLB:
httpHeaderName: x-user-id # or httpCookie, useSourceIp, httpQueryParameterNameEnvoy hashes the specified key and maps it to a point on a hash ring where each endpoint occupies a range. When endpoints are added/removed, only a fraction of keys get remapped (minimizing disruption).
Use cases: sticky sessions for stateful services, cache affinity (user X always hits the same cache node), WebSocket connections.
Caveat: if the hashed endpoint is ejected by outlier detection, the request falls through to the next endpoint on the ring β no error, just a different backend.