Envoy’s core strength isn’t just routing traffic; it’s about arbitrarily reconfiguring its entire understanding of the network topology, including what services exist, where they are, and how to talk to them, all without a single restart.
Let’s see this in action. Imagine we have a simple frontend service that needs to talk to a backend service. We’ll configure Envoy to act as a proxy for frontend, so frontend thinks it’s talking directly to backend, but Envoy is intercepting and forwarding.
Here’s a complete static configuration:
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:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/api/v1/users"
route:
cluster: users_service
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: users_service
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: users_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 192.168.1.100
port_value: 80
- name: another_service
connect_timeout: 1s
type: STATIC
load_assignment:
cluster_name: another_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 192.168.1.101
port_value: 9090
- endpoint:
address:
socket_address:
address: 192.168.1.102
port_value: 9090
admin:
access_log_path: /tmp/envoy.log
address:
socket_address:
address: 127.0.0.1
port_value: 9901
In this config, we’ve set up two main parts: listeners and clusters.
The listener section defines listener_0. It’s listening on all interfaces (0.0.0.0) on port 8080. When a request comes in, the http_connection_manager filter is invoked. This filter is the brain for L7 routing. It looks at the request’s Host header and path. Here, any request to * (meaning any host) with a path starting with /api/v1/users will be routed to a cluster named users_service. The stat_prefix is used for Prometheus metrics.
The clusters section defines the destinations. We have users_service. Its type is LOGICAL_DNS. This means Envoy will resolve users_service via DNS (if it were a real DNS name) or, in this static config, it’s effectively hardcoded to resolve to 192.168.1.100:80. Envoy will then load balance across its endpoints using ROUND_ROBIN. We also define another_service with type: STATIC, which means its endpoints are explicitly listed: 192.168.1.101:9090 and 192.168.1.102:9090.
The admin section is crucial for observability and control. It gives you a web interface (on 127.0.0.1:9901 in this case) to inspect Envoy’s configuration, statistics, and even dynamically update it (though this config is static).
When frontend sends a request like GET /api/v1/users/123 to localhost:8080 (where Envoy is listening), Envoy’s http_connection_manager sees the /api/v1/users prefix, consults its route_config, and determines that this request should go to the users_service cluster. It then selects an endpoint from users_service (in this case, 192.168.1.100:80) and forwards the request. The users_service receives the request as if it came directly from Envoy, not from frontend.
The connect_timeout values are critical for how quickly Envoy abandons a connection attempt to an upstream host. A lower value, like 0.25s for users_service, means Envoy is aggressive about failing fast if the backend is unresponsive. A higher value, like 1s for another_service, gives backends more time to respond.
What most people don’t realize is that Envoy’s type: LOGICAL_DNS doesn’t just do DNS lookups. When you provide an address in the load_assignment for a LOGICAL_DNS cluster, Envoy will treat that address as the sole resolved endpoint for that logical DNS name. It’s a way to bootstrap a DNS-like name in a static configuration without relying on an actual DNS server for that specific cluster.
After successfully routing traffic, you’ll likely want to explore Envoy’s load balancing policies beyond ROUND_ROBIN, such as LEAST_REQUEST or RING_HASH.