VirtualService
An Istio custom resource that defines how requests are routed to services. It replaces the default Kubernetes Service routing with precise traffic rules - including weighted splits, header matching, retries, timeouts, and fault injection.
What is a VirtualService
A Kubernetes Service by default routes requests to all matching pods equally, round-robin. A VirtualService changes this - it intercepts routing at the sidecar level and applies more sophisticated rules before forwarding.
Without VirtualService:Request to orders-service → Kubernetes Service → random pod (round-robin) With VirtualService:Request to orders-service → Envoy sidecar reads VirtualService rules→ if header x-canary: true → route to v2 subset→ else if 10% probability → route to v2 subset→ else → route to v1 subsetVirtualService Structure
apiVersion: networking.istio.io/v1kind: VirtualServicemetadata: name: orders-service namespace: productionspec: hosts: - orders-service ## which service this applies to http: - match: ## optional matching rules - headers: x-canary: exact: "true" route: - destination: host: orders-service subset: v2 ## defined in DestinationRule - route: ## default route (no match required) - destination: host: orders-service subset: v1 weight: 90 - destination: host: orders-service subset: v2 weight: 10What You Can Match On
HTTP method: GET, POST, PUT, DELETEURI path: prefix, exact, regexHeaders: exact value, prefix, regexQuery params: exact value, regexSource labels: which calling service triggers this ruleWhat You Can Do in the Route
Weight: exact percentage to each destination (must sum to 100)Timeout: maximum time to wait for a responseRetries: how many times to retry on failure, which errors trigger retryFault injection: inject delays or errors for chaos testing## Example with timeout and retrieshttp: - route: - destination: host: payments-service subset: v1 timeout: 3s retries: attempts: 3 perTryTimeout: 1s retryOn: 5xx,connect-failureVirtualService Requires DestinationRule for Subsets
If your VirtualService references subset names (v1, v2), you must have a DestinationRule that defines those subsets. Missing DestinationRule causes 503 errors immediately.
## Check for configuration issues before applyingistioctl analyze virtualservice.yamlRememberVirtualService controls HOW traffic is routed. DestinationRule controls WHERE traffic goes (which pods are in each subset) and WHAT policies apply (circuit breaking, connection limits). You need both for traffic splitting to work.
Common MistakeApplying a VirtualService that references subset v2 before creating the DestinationRule that defines v2. Istio returns 503 for all traffic matching that route. Always apply the DestinationRule first, or use
istioctl analyzeto catch this error before deployment.
Frequently Asked Questions
How does a VirtualService relate to a plain Kubernetes Service — do you still need both?
Yes — a Kubernetes Service still provides the stable DNS name and endpoint set, but Istio intercepts traffic before it hits kube-proxy's default round-robin routing and applies the VirtualService's rules instead: weighted routing across subsets (defined via a companion DestinationRule), HTTP header-based routing, retry/timeout policies, and fault injection for chaos testing. Without a VirtualService, Istio just passes through to standard Service behavior.
What's a common mistake when writing Istio VirtualServices for canary routing?
Defining subset weights in the VirtualService without a matching DestinationRule that actually declares those subsets (e.g. `version: v1` / `version: v2` based on pod labels) — the VirtualService will fail to apply the split, often silently defaulting to routing errors. Also common: forgetting that route rules are evaluated in order and the first match wins, so a broad catch-all rule placed before a specific header-match rule will shadow it entirely.