Envoy failed to route your request because it couldn’t find a suitable upstream cluster to send it to, despite receiving a valid request from the client.

Here’s why that can happen, and how to fix it:

1. Missing or Incorrectly Configured service_name in the Virtual Host

This is the most common culprit. Envoy uses the service_name in your virtual host configuration to match incoming requests to the correct upstream cluster. If it’s misspelled, missing, or doesn’t match the HOST header of your request, Envoy will throw this error.

  • Diagnosis: Check your Envoy configuration file (typically envoy.yaml or a file included in it). Look for the virtual_hosts section.

    static_resources:
      listeners:
      - name: listener_0
        address:
          socket_address: { address: 0.0.0.0, port_value: 10000 }
        filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              stat_prefix: ingress_http
              route_config:
                name: route_config_0
                virtual_hosts:
                - name: some_service_vhost
                  domains: ["my-api.example.com"]
                  # THIS is what you're looking for:
                  routes:
                  - match: { prefix: "/" }
                    route:
                      cluster: my_app_cluster
    

    Now, look at your actual HTTP request’s HOST header. Does it match any of the domains in your virtual_hosts? If not, or if there’s no domains section and you’re expecting a specific host, Envoy won’t know which virtual_host to use.

  • Fix: Ensure the domains in your virtual_hosts accurately reflect the HOST header your clients are sending. If you want to match any host for a given virtual host, you can use * as the domain.

    virtual_hosts:
    - name: some_service_vhost
      domains: ["my-api.example.com", "*.example.com"] # Added wildcard
      routes:
      - match: { prefix: "/" }
        route:
          cluster: my_app_cluster
    

    Or, if you have no specific domain requirements and want this virtual host to catch everything not matched by other virtual hosts:

    virtual_hosts:
    - name: default_catchall_vhost
      domains: ["*"] # Catches any host
      routes:
      - match: { prefix: "/" }
        route:
          cluster: my_app_cluster
    

    This works because Envoy uses the domains list to find the first virtual_host that matches the incoming request’s HOST header.

2. The cluster Name in the Route Doesn’t Exist

Even if Envoy selects the correct virtual_host, it still needs a cluster defined that matches the cluster name specified in the route.

  • Diagnosis: In your envoy.yaml, within the route_config for the relevant virtual_host, you’ll see a route with a cluster field.

    route:
      cluster: my_app_cluster # This name must exist in static_resources.clusters
    

    Now, scroll down to the static_resources.clusters section and verify that a cluster with the exact name my_app_cluster is defined.

    static_resources:
      clusters:
      - name: my_app_cluster # <-- This must match the route's cluster name
        connect_timeout: 0.25s
        type: STRICT_DNS
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: my_app_cluster
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address: { address: 10.0.0.1, port_value: 8080 }
    
  • Fix: Either rename the cluster in your route to match an existing cluster, or create/rename the cluster definition in static_resources.clusters to match the name used in the route.

    # ... in route_config ...
    route:
      cluster: my_existing_app_cluster # Renamed to match a defined cluster
    
    # ... in static_resources.clusters ...
    clusters:
    - name: my_existing_app_cluster # <-- Now this matches
      # ... rest of cluster config ...
    

    This works because Envoy maintains a catalog of defined clusters by name. If a route references a cluster name not in this catalog, it has nowhere to send the request.

3. Wildcard Domains (*) in Virtual Hosts Without Specificity

If you have multiple virtual hosts, and one uses a broad wildcard domain like *, it can "catch" requests that were intended for a more specific virtual host if the wildcard is listed before the specific one in the configuration. Envoy processes virtual hosts in the order they appear.

  • Diagnosis: Examine the order of your virtual_hosts in envoy.yaml. If you see a domains: ["*"] entry, check if it appears before a more specific domain like domains: ["api.example.com"].

    virtual_hosts:
    - name: catchall_vhost # This might be matching too early
      domains: ["*"]
      routes:
      - match: { prefix: "/" }
        route:
          cluster: fallback_cluster
    
    - name: specific_api_vhost # This might be skipped
      domains: ["api.example.com"]
      routes:
      - match: { prefix: "/" }
        route:
          cluster: api_cluster
    
  • Fix: Reorder your virtual_hosts so that the most specific domains appear first, followed by less specific ones, and finally the wildcard catch-all.

    virtual_hosts:
    - name: specific_api_vhost # Now this is checked first for api.example.com
      domains: ["api.example.com"]
      routes:
      - match: { prefix: "/" }
        route:
          cluster: api_cluster
    
    - name: catchall_vhost # This will only catch requests not matching api.example.com
      domains: ["*"]
      routes:
      - match: { prefix: "/" }
        route:
          cluster: fallback_cluster
    

    This ensures Envoy attempts to match the most precise domain first. If a match is found, it uses that virtual host; otherwise, it proceeds to the next virtual host, including the wildcard.

4. Invalid Cluster Type or Configuration

While less common for "No Cluster Found," a malformed cluster definition can lead Envoy to discard it, effectively making it "not found." This could be due to incorrect endpoint addresses, invalid type settings, or missing required fields.

  • Diagnosis: Scrutinize the static_resources.clusters section for the cluster name referenced in your route. Ensure the type is valid (e.g., STRICT_DNS, LOGICAL_DNS, STATIC, ORIGINAL_DST). Check that load_assignment or hosts fields are correctly populated with valid IP addresses and ports.

    # Example of a potentially problematic cluster config
    clusters:
    - name: my_broken_cluster
      connect_timeout: 0.25s
      type: INVALID_TYPE # <-- Invalid type
      # ... other fields ...
    

    Or:

    clusters:
    - name: my_broken_cluster
      connect_timeout: 0.25s
      type: STRICT_DNS
      load_assignment:
        cluster_name: my_broken_cluster
        endpoints:
        - lb_endpoints:
          - endpoint:
              address:
                socket_address: { address: "not_an_ip", port_value: 8080 } # <-- Invalid address
    
  • Fix: Correct the type field to a valid option. Ensure IP addresses are valid and ports are numeric. For STRICT_DNS or LOGICAL_DNS, verify the DNS name resolves correctly. For STATIC or ORIGINAL_DST, ensure the addresses are correctly specified.

    clusters:
    - name: my_fixed_cluster
      connect_timeout: 0.25s
      type: STRICT_DNS # <-- Corrected type
      load_assignment:
        cluster_name: my_fixed_cluster
        endpoints:
        - lb_endpoints:
          - endpoint:
              address:
                socket_address: { address: "192.168.1.100", port_value: 8080 } # <-- Valid address
    

    Envoy performs validation on cluster configurations during startup. If a cluster definition is invalid, Envoy might not register it at all, leading to the "No Cluster Found" error when a route attempts to use it.

5. Dynamic Configuration Not Loaded (if using EDS/Aggregated Discovery Service)

If you’re using dynamic configuration sources like EDS (Endpoint Discovery Service) or ADS (Aggregated Discovery Service) and your configuration server isn’t pushing cluster information, Envoy won’t know about the clusters.

  • Diagnosis: Check the Envoy logs for any errors related to connecting to the xDS server or receiving updates. Use curl to inspect Envoy’s internal stats.

    curl -s http://localhost:9901/stats/prometheus | grep envoy_cluster_manager_sds_fetch_total
    

    If the sds_fetch_total counters aren’t incrementing, Envoy isn’t successfully fetching configuration. Also, check the status of your xDS server (e.g., Istio’s Pilot, Consul, etc.).

  • Fix: Ensure your xDS server is running, accessible from Envoy, and correctly configured to push the necessary cluster definitions. Verify network connectivity between Envoy and the xDS server.

    # Example: Ensure your Istio ingress gateway pods are running and healthy
    kubectl get pods -n istio-system
    

    This works by Envoy establishing a gRPC connection to the xDS API server and subscribing to updates for cluster configurations. If this connection or the update process fails, Envoy’s cluster registry remains empty or incomplete.

6. Empty or Missing clusters Section in Static Configuration

In a purely static configuration, if the static_resources.clusters section is entirely missing or empty, Envoy has no clusters to reference, regardless of the routing rules.

  • Diagnosis: Open your envoy.yaml and look for static_resources. Does it contain a clusters key? If so, is the list of clusters under it empty?

    static_resources:
      # clusters: [] # <-- This would be an empty cluster list
      # OR
      # This entire 'clusters' section is missing
    
  • Fix: Add your cluster definitions under the static_resources.clusters key.

    static_resources:
      clusters:
      - name: my_app_cluster
        connect_timeout: 0.25s
        type: STRICT_DNS
        load_assignment:
          cluster_name: my_app_cluster
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address: { address: 10.0.0.1, port_value: 8080 }
      # ... other clusters ...
    

    Envoy needs a populated clusters list within its static resources to know what upstream services are available to route requests to.

The next error you’ll likely encounter if you’ve fixed this is a 503 Service Unavailable if the endpoints within your now-found cluster are unhealthy or unreachable.

Want structured learning?

Take the full Envoy course →