Envoy’s routing capabilities, especially within a single Availability Zone (AZ), are far more granular and dynamic than a simple load balancer.

Let’s see it in action. Imagine you have two services, frontend and backend, running on different ports but within the same AZ.

# Envoy Configuration Snippet
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 8080
    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
          codec_type: auto
          route_config:
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/api/v1/users"
                route:
                  cluster: backend_users
              - match:
                  prefix: "/"
                route:
                  cluster: frontend_web
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: frontend_web
    connect_timeout: 0.25s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: frontend_web
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 10.0.1.10  # Example IP within the AZ
                port_value: 8001
        - endpoint:
            address:
              socket_address:
                address: 10.0.1.11  # Another example IP within the AZ
                port_value: 8001
  - name: backend_users
    connect_timeout: 0.25s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: backend_users
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 10.0.1.20  # Example IP within the AZ
                port_value: 9000
        - endpoint:
            address:
              socket_address:
                address: 10.0.1.21  # Another example IP within the AZ
                port_value: 9000

In this setup, Envoy acts as a local proxy. It listens on port 8080. When a request comes in for /api/v1/users, Envoy consults its route_config. It finds a match for the /api/v1/users prefix and forwards the request to the backend_users cluster. If the request path doesn’t match /api/v1/users (e.g., it’s just / or /index.html), it falls through to the next route, which matches / and sends the request to the frontend_web cluster.

The clusters define the actual upstream services. Each cluster has load_assignment which lists the endpoints – the IP addresses and ports of the service instances. Envoy uses the specified lb_policy (here ROUND_ROBIN) to distribute requests across these endpoints. The type: STRICT_DNS means Envoy will resolve the service names (though here we’ve used IPs directly for simplicity within an AZ context) and maintain a cache.

This isn’t just about directing traffic; it’s about intelligent distribution and service discovery within your infrastructure. You can define sophisticated routing rules based on request headers, query parameters, or even the source IP address, all without those requests ever leaving the Availability Zone. This significantly reduces latency and egress costs compared to routing traffic across AZs unnecessarily.

Envoy’s routing is not limited to simple path matching. You can use regex_match for more complex URL structures, safe_regex_match for enhanced safety, or even match on specific request headers using header_match. For example, to send requests with a specific x-version: beta header to a separate beta_backend cluster, you’d add another route:

              - match:
                  prefix: "/api/v1/users"
                  headers:
                  - name: "x-version"
                    exact_match: "beta"
                route:
                  cluster: beta_backend

This allows for blue-green deployments, canary releases, or A/B testing managed entirely by Envoy’s routing logic, even for services co-located within the same AZ. The connect_timeout is crucial for ensuring Envoy doesn’t wait indefinitely for an unhealthy upstream.

When configuring Envoy to route traffic within an AZ, especially for services that might have rapidly changing IP addresses (like those managed by Kubernetes or other orchestrators), using type: EDS (Endpoint Discovery Service) instead of STRICT_DNS is often more robust. EDS allows Envoy to dynamically receive lists of healthy endpoints from a control plane, rather than relying solely on DNS resolution. This offers more immediate updates and better health checking integration.

The next step is to understand how Envoy handles health checking and circuit breaking to ensure the reliability of this intra-AZ traffic routing.

Want structured learning?

Take the full Envoy course →