Skip to main content

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.

◈ DIAGRAM
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 subset

VirtualService Structure

YAML
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: orders-service
namespace: production
spec:
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: 10

What You Can Match On

TEXT
HTTP method: GET, POST, PUT, DELETE
URI path: prefix, exact, regex
Headers: exact value, prefix, regex
Query params: exact value, regex
Source labels: which calling service triggers this rule

What You Can Do in the Route

TEXT
Weight: exact percentage to each destination (must sum to 100)
Timeout: maximum time to wait for a response
Retries: how many times to retry on failure, which errors trigger retry
Fault injection: inject delays or errors for chaos testing
YAML
## Example with timeout and retries
http:
- route:
- destination:
host: payments-service
subset: v1
timeout: 3s
retries:
attempts: 3
perTryTimeout: 1s
retryOn: 5xx,connect-failure

VirtualService 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.

Bash
## Check for configuration issues before applying
istioctl analyze virtualservice.yaml
Remember

VirtualService 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 Mistake

Applying 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 analyze to 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.