Mastering Gray Release with Envoy: Weight, Header, and Cookie Routing
This article explains how to implement safe, incremental gray releases in a microservice architecture using Envoy Proxy, covering weight‑based traffic splitting, header‑based canary routing, and cookie‑based routing with full configuration examples and testing commands.
Why Gray Release Matters
In today’s microservice world, safely rolling out new service versions is critical. Gray release (canary deployment) lets you expose a new version to a small user subset, verify stability, and gradually increase traffic, reducing the impact of potential failures.
Envoy as the Ideal Proxy
Envoy Proxy’s high performance, extensibility, and powerful routing capabilities make it a perfect choice for implementing gray releases. The following sections show how to achieve flexible gray releases using weight‑based, header‑based, and cookie‑based routing rules.
1. Weight‑Based Gray Release
Route 80% of traffic to the old service and 20% to the new service.
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
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
weighted_clusters:
clusters:
- name: simple_cluster_old
weight: 80
- name: simple_cluster_new
weight: 20
clusters:
- name: simple_cluster_new
lb_policy: ROUND_ROBIN
type: STATIC
load_assignment:
cluster_name: simple_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 172.139.20.170, port_value: 8090 }
- name: simple_cluster_old
lb_policy: ROUND_ROBIN
type: STATIC
load_assignment:
cluster_name: simple_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 172.139.20.3, port_value: 8090 }
- endpoint:
address:
socket_address: { address: 172.139.20.92, port_value: 8090 }Test the configuration by repeatedly calling the service endpoint and observing the returned version information.
$ for i in {1..5}; do curl http://localhost:10000/version; echo; done
{ "BuildTime": "2025-09-09", "CommitID": "09ee5a64f9a2ebdded0fafbbae5e7dcfb3bf424c", "Version": "v1.5.1" }
# Adjust traffic split to 80/20
$ curl -XPOST http://localhost:9901/runtime_modify?routing.traffic_shift.simple=80
OK2. Header‑Based Gray Release
Requests containing the header x-canary=true are routed to the new version; all others go to the old version.
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
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
headers:
- name: "x-canary"
exact_match: "true"
route:
cluster: simple_cluster_new
- match:
prefix: "/"
route:
cluster: simple_cluster_old
clusters:
- name: simple_cluster_new
lb_policy: ROUND_ROBIN
type: STATIC
load_assignment:
cluster_name: simple_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 172.139.20.170, port_value: 8090 }
- name: simple_cluster_old
lb_policy: ROUND_ROBIN
type: STATIC
load_assignment:
cluster_name: simple_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 172.139.20.3, port_value: 8090 }
- endpoint:
address:
socket_address: { address: 172.139.20.92, port_value: 8090 }3. Cookie‑Based Gray Release
Requests with a cookie canary=always are sent to the new version; others go to the old version.
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
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
headers:
- name: "Cookie"
exact_match: "canary=always"
route:
cluster: simple_cluster_new
- match:
prefix: "/"
route:
cluster: simple_cluster_old
clusters:
- name: simple_cluster_new
lb_policy: ROUND_ROBIN
type: STATIC
load_assignment:
cluster_name: simple_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 172.139.20.170, port_value: 8090 }
- name: simple_cluster_old
lb_policy: ROUND_ROBIN
type: STATIC
load_assignment:
cluster_name: simple_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 172.139.20.3, port_value: 8090 }
- endpoint:
address:
socket_address: { address: 172.139.20.92, port_value: 8090 }Tip: Larger sample sizes make traffic‑split percentages more accurate.
Conclusion
Gray release is not a one‑off task but an ongoing safety mechanism for continuous delivery. Envoy’s powerful routing and flexible configuration model provide a solid foundation for implementing reliable canary deployments.
Linux Ops Smart Journey
The operations journey never stops—pursuing excellence endlessly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
