The Istio ingress gateway is failing to route traffic to your services because it can’t find a matching VirtualService for the incoming request’s host and path.

This usually means one of two things: either the VirtualService isn’t being applied to the correct namespace, or its configuration doesn’t precisely match the request.

Common Causes and Fixes

1. VirtualService in the Wrong Namespace

  • Diagnosis: Check the namespace of your VirtualService resource. Istio’s Gateway resource is namespace-scoped, and the VirtualService must be in the same namespace as the Gateway it’s intended to bind to, or the VirtualService must explicitly reference the Gateway’s namespace if they are different (which is less common for basic setups).

    kubectl get virtualservices -A
    

    Look for your VirtualService and confirm its namespace. If your Gateway is in istio-system, your VirtualService must also be in istio-system (or specify gateways: - "istio-system/my-gateway" within the VirtualService if it’s in another namespace).

  • Fix: If the VirtualService is in the wrong namespace, move it to the namespace where your Gateway resides.

    # Example: If VirtualService is in 'default' and Gateway is in 'istio-system'
    kubectl get virtualservice my-virtual-service -n default -o yaml | \
    sed "s/namespace: default/namespace: istio-system/" | \
    kubectl apply -f -
    # Then delete the old one
    kubectl delete virtualservice my-virtual-service -n default
    

    This ensures the VirtualService is discoverable by the Gateway in the same scope.

2. Mismatched Host in VirtualService and Request

  • Diagnosis: The hosts field in your VirtualService must exactly match the Host header of the incoming HTTP request. If you’re accessing my-app.example.com, your VirtualService needs hosts: ["my-app.example.com"]. Wildcards are supported (e.g., hosts: ["*.example.com"]).

    # Capture traffic hitting the ingress gateway and inspect Host header
    kubectl tcpdump -nL -i <ingress-gateway-node-interface> port 80 or port 443 -s 0 -w - | \
    tcpdump -r - 'tcp port 80 or tcp port 443' -vvv
    

    Alternatively, use Istio’s traffic mirroring or istioctl proxy-config listeners on the ingress gateway pod to see what hosts it’s advertising.

  • Fix: Update the hosts field in your VirtualService to match the expected Host header.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: my-app-vs
      namespace: default # Must match Gateway namespace or be specified in Gateway
    spec:
      hosts:
      - "my-app.example.com" # Ensure this matches your request's Host header
      gateways:
      - my-ingress-gateway # Reference the Gateway
      http:
      - route:
        - destination:
            host: my-app-service
            port:
              number: 80
    

    This provides the precise binding Istio needs to direct traffic.

3. Mismatched Path in VirtualService’s match Rule

  • Diagnosis: If your VirtualService has an http section with match rules (e.g., uri.prefix: "/api"), the incoming request’s URI must also match this prefix. A request to / won’t match a rule expecting /api.

    # Again, use tcpdump or Istio's debugging tools on the ingress gateway
    # to inspect the request URI.
    kubectl logs <ingress-gateway-pod> -c istio-proxy -n istio-system -f
    

    Look for RST_STREAM or 404 responses and check the upstream cluster name. If it’s Passthrough or BlackHole, the request didn’t match any defined route.

  • Fix: Adjust the match rules in your VirtualService to accurately reflect the request path.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: my-app-vs
      namespace: default
    spec:
      hosts:
      - "my-app.example.com"
      gateways:
      - my-ingress-gateway
      http:
      - match:
        - uri:
            prefix: "/api" # This must match the request path, e.g., /api/users
        route:
        - destination:
            host: my-api-service
            port:
              number: 8080
    

    This ensures that requests with paths starting with /api are correctly routed.

4. Empty or Incorrect Gateway Reference in VirtualService

  • Diagnosis: The VirtualService must explicitly reference the Gateway it’s supposed to be bound to using the gateways field. If this field is missing, or references a Gateway that doesn’t exist or is in the wrong namespace, the VirtualService won’t be activated.

    kubectl get gateway my-ingress-gateway -n istio-system -o yaml
    kubectl get virtualservice my-app-vs -n default -o yaml
    

    Verify the gateways field in the VirtualService points to the correct Gateway name and namespace.

  • Fix: Add or correct the gateways field in your VirtualService.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: my-app-vs
      namespace: default
    spec:
      hosts:
      - "my-app.example.com"
      gateways:
      - istio-system/my-ingress-gateway # Explicitly specify namespace/name
      http:
      - route:
        - destination:
            host: my-app-service
            port:
              number: 80
    

    This linkage is crucial for Istio to associate the routing rules with the ingress point.

5. Gateway Resource Not Applied or Incorrectly Configured

  • Diagnosis: The Gateway resource itself must be correctly applied and configured to listen on the expected ports and hosts. If the Gateway isn’t active, no VirtualService can bind to it.

    kubectl get gateway my-ingress-gateway -n istio-system -o yaml
    kubectl describe gateway my-ingress-gateway -n istio-system
    

    Check the selector on the Gateway to ensure it matches the labels on your ingress gateway pods (usually istio: ingressgateway). Also, verify the servers section defines the correct ports (e.g., port: { number: 80, name: http }).

  • Fix: Ensure your Gateway resource is applied and has the correct selector and server configuration.

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: my-ingress-gateway
      namespace: istio-system
    spec:
      selector:
        istio: ingressgateway # Must match ingress gateway pod labels
      servers:
      - port:
          number: 80
          name: http
          protocol: HTTP
        hosts:
        - "*" # Or your specific domain
    

    This makes the ingress gateway ready to receive traffic and bind VirtualServices.

6. Typo in Destination Service Hostname

  • Diagnosis: Within the VirtualService’s http route, the destination.host must be the actual service name as known by Istio’s internal service registry (which is typically the Kubernetes service name). A typo here means Istio tries to route to a non-existent service.

    kubectl get svc -n <your-service-namespace>
    kubectl get virtualservice my-app-vs -n default -o yaml
    

    Compare the destination.host in your VirtualService with the output of kubectl get svc.

  • Fix: Correct the destination.host in the VirtualService to match the actual Kubernetes service name.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: my-app-vs
      namespace: default
    spec:
      hosts:
      - "my-app.example.com"
      gateways:
      - my-ingress-gateway
      http:
      - route:
        - destination:
            host: my-app-service.my-namespace.svc.cluster.local # Fully qualified or short name
            port:
              number: 80
    

    This ensures traffic is directed to the correct backing service.

7. Istio Control Plane Not Healthy

  • Diagnosis: If Istiod (the control plane) is unhealthy, it won’t be able to push configuration updates to the data plane (Envoy proxies in the gateway and sidecars). This can lead to stale configurations where new VirtualServices aren’t recognized.

    kubectl get pods -n istio-system
    kubectl logs <istiod-pod-name> -n istio-system -c discovery
    

    Look for any Error, CrashLoopBackOff, or repeated restarts in the istio-system namespace, particularly for istiod.

  • Fix: Troubleshoot and resolve the underlying issues causing Istiod to be unhealthy. This might involve resource constraints, network policies blocking communication, or bugs. Restarting Istiod pods can sometimes resolve transient issues.

    kubectl rollout restart deployment istiod -n istio-system
    

    A healthy Istiod is critical for distributing all Istio configurations.

After fixing these, you’ll likely encounter a "503 Service Unavailable" if the backend service itself is not running or reachable.

Want structured learning?

Take the full Istio course →