Weighted Canary Routing with Envoy and Kong
The configuration for a weighted split is three lines on either gateway. Everything that makes it work in production is around those three lines: whether a user stays on one version, whether the realised split matches the configured one, and how quickly a weight change reaches every node. This page covers all of it for Envoy 1.32+ and Kong 3.x.
Prerequisite concepts
This assumes the mechanism comparison and measurement discipline in traffic splitting and progressive delivery.
Random sampling versus sticky assignment
# Envoy 1.32+ — sticky assignment by hashing a header
routes:
- match: { prefix: "/v2/orders" }
route:
weighted_clusters:
clusters:
- { name: orders_stable, weight: 95 }
- { name: orders_canary, weight: 5 }
hash_policy:
- header: { header_name: "x-user-id" } # set by the gateway from a claim
terminal: true
timeout: 3s
# Kong 3.x — hash-on at the upstream, weights on the targets
upstreams:
- name: orders-upstream
hash_on: header
hash_on_header: x-user-id
hash_fallback: none # no fallback: a request without the header is rejected
targets:
- { target: orders-stable.internal:8080, weight: 95 }
- { target: orders-canary.internal:8080, weight: 5 }
hash_fallback: none is deliberate. A fallback to round-robin means requests missing the header are distributed randomly, which quietly reintroduces the behaviour hashing was meant to remove — and it will be the anonymous traffic, which is often the traffic you least want split mid-flow.
Weight changes and how fast they arrive
Verifying the split is what you configured
The realised split is an observation, not an assumption, and the first thing to check when a canary shows no signal at all.
# share of requests served by each release, last 5 minutes
sum by (release) (rate(gateway_requests_total{route="orders"}[5m]))
/ ignoring(release) group_left
sum (rate(gateway_requests_total{route="orders"}[5m]))
A canary at a configured five percent that measures at zero point three means either the hash is concentrating traffic elsewhere, or the canary cluster has no healthy endpoints and requests are silently falling back. The second case is common and produces a rollout that looks perfect because nothing was ever tested.
Decision matrix
| Situation | Choose |
|---|---|
| Stateless reads | random weighting |
| Multi-step user flow | hash on a stable user attribute |
| Per-tenant rollout | hash on the tenant, or route on the claim |
| Very low traffic route | header targeting instead of weights |
| Rollback must be under a minute | a control plane that pushes, not one that polls |
Gotchas and failure signals
A canary cluster with no healthy endpoints absorbs no traffic and reports no errors. Always gate on observed share.
Sticky assignment plus a changing user population shifts the realised split over the day as different cohorts become active. Compare rates, never cumulative counts.
Weights on targets versus weights on clusters differ in what health checking does to them: an unhealthy target is skipped and its share redistributed, which can silently move the whole canary share to stable.
Response headers naming the release are worth adding — they make a support conversation a one-line answer, and they cost nothing.
Ramp shape, and why equal steps are the wrong default
The obvious ramp is a sequence of equal steps: five, ten, twenty, fifty, a hundred. It is also the one that spends the most exposure for the least information.
Each stage exists to answer a question, and the questions are not equally hard. The first stage asks whether the new version works at all under real traffic, which needs very little data — a broken build fails immediately. The middle stages ask whether it is subtly worse, which needs the most data and therefore the most time. The last stage asks whether it holds at full load, which needs the least analysis and the most caution.
A shape that reflects that spends a short stage at a small weight, a long stage in the middle where the statistics are gathered, and a short final step. In practice: five percent for ten minutes to catch the obvious, twenty-five percent for an hour to gather a sample, then a hundred. Three stages, one long one, rather than six equal ones that each prove a little and take all afternoon.
Validation
- Observed traffic share matches the configured weight, checked before advancing
- Sticky assignment used where flows span requests, with no random fallback
- Propagation time for a weight change measured and written in the runbook
- Canary cluster health verified independently of the split
- Release identifier present on both metrics and responses
FAQ
Will a user stay on one version during a weighted rollout?
Not by default. Weights are evaluated per request, so a user making six requests will usually touch both versions. That is fine for stateless reads and wrong for a multi-step flow where the two versions disagree. Hash a stable attribute such as a user identifier into the choice to assign each user to one version, and set no random fallback for requests missing the attribute.
Why does my canary show no errors and no signal?
Check the observed traffic share before anything else. A canary cluster with no healthy endpoints absorbs no traffic, produces no errors and lets an automated rollout advance through every stage having tested nothing. Gate each stage on measured share rather than on elapsed time — it is the single most common silent failure in canary pipelines.
Why does the realised split not match the configured weight?
Usually hashing. Hash distributions are uneven at small percentages, so a five percent ring can serve three or eight percent depending on which users are active, and the share drifts through the day as cohorts change. Compare rates rather than cumulative counts, and expect the sample to take longer than the nominal weight suggests.
How fast can I roll back?
As fast as your configuration propagates, which ranges from sub-second on a streaming control plane to a full redeploy for a baked declarative file. Measure it once in production and write the number in the runbook beside the rollback command, because during an incident the difference between thirty seconds and five minutes is the whole conversation.
Parent: Traffic Splitting & Progressive Delivery
Related
- Traffic Splitting & Progressive Delivery — the mechanisms and measurement behind these weights.
- Automated Rollback on SLO Breach — closing the loop on the weight this page sets.
- Header-Based Dark Launches and Shadow Traffic — what to do when a route has too little traffic to canary.