Envoy doesn’t actually "balance" traffic in the way you might think of a traditional load balancer; it’s more about intelligently directing requests based on a rich set of criteria.
Let’s see Envoy in action. Imagine you have a microservice architecture. You want to route traffic to different versions of your product-catalog service based on the x-user-segment header.
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
route_config:
name: local_route
virtual_hosts:
- name: product_catalog_service
domains: ["product-catalog.example.com"]
routes:
- match:
prefix: "/"
headers:
- name: "x-user-segment"
exact_match: "premium"
route:
cluster: product_catalog_premium
- match:
prefix: "/"
headers:
- name: "x-user-segment"
exact_match: "beta"
route:
cluster: product_catalog_beta
- match:
prefix: "/"
route:
cluster: product_catalog_default
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: product_catalog_default
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: product_catalog_default
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.1
port_value: 8001
- name: product_catalog_premium
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: product_catalog_premium
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.2
port_value: 8002
- name: product_catalog_beta
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: product_catalog_beta
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.3
port_value: 8003
In this configuration, Envoy is acting as a sophisticated router. The listener_0 listens on port 8080. The http_connection_manager filter intercepts incoming HTTP requests. Within its route_config, we define a virtual_host for product-catalog.example.com.
The routes array is where the magic happens. Envoy evaluates these routes in order.
- If a request has the header
x-user-segmentwith the exact valuepremium, it’s routed to theproduct_catalog_premiumcluster. - If the first match fails, Envoy checks the next route. If the
x-user-segmentheader isbeta, it goes toproduct_catalog_beta. - If neither of the preceding conditions is met, the final, catch-all route directs traffic to
product_catalog_default.
Each cluster (product_catalog_default, product_catalog_premium, product_catalog_beta) defines a group of upstream service instances. LOGICAL_DNS means Envoy will resolve the provided hostname (though here we’ve used IP addresses directly for clarity, which is less common for dynamic environments) and ROUND_ROBIN is the load balancing policy for distributing requests among the endpoints within that cluster.
The key is that Envoy’s routing rules are evaluated sequentially. The first match that satisfies the incoming request determines the route. This allows for complex traffic shaping: you can send a small percentage of traffic to a new version using weighted routing, route based on request headers, paths, methods, source IPs, and even runtime configuration.
What most people don’t realize is that Envoy’s routing rules are not just about matching headers; they can also involve regular expressions for more flexible string matching on paths or headers, and you can combine multiple match criteria within a single route. For example, you could route requests for /api/v2/users from premium users to a specific cluster, while all other requests to /api/v2/users go elsewhere.
This advanced routing capability is what enables blue-green deployments, canary releases, A/B testing, and fine-grained traffic control in modern microservice architectures.
The next step is often exploring how to dynamically update these routing rules without restarting Envoy.