Using Traefik Mesh TrafficSplit for Canary Releases in Kubernetes
Learn how to use Traefik Mesh TrafficSplit to perform weighted canary releases in Kubernetes, with a concrete example, verification steps, and limitations.
25 Oct 2025, 18:31 UTC

Problem: Safely shifting traffic to a new version
When rolling out a new version of a service, you often want to send only a fraction of live traffic to it while keeping the majority on the stable release. Doing this with Kubernetes alone usually requires changing selectors, creating separate Services, or using an external service mesh. If your cluster already runs Traefik Mesh, you can leverage its built‑in TrafficSplit custom resource to adjust routing at the sidecar level without touching Deployments or Services.
Thesis
Traefik Mesh TrafficSplit lets you define weighted routing between multiple versions of a service, observe the split via built‑in metrics, and combine it with other policies (Retry, Timeout, CircuitBreaker) to shape the canary’s behavior independently.
How TrafficSplit works
Traefik Mesh injects an Envoy sidecar into each pod that carries the traefikmesh.io/sidecar-injection: enabled annotation. The TrafficSplit CR tells the sidecar how to distribute inbound requests among the listed backends. The controller watches the CR, updates Envoy’s routing table, and the new weights take effect after a short reconciliation period (typically a few seconds).
Worked example: 90/10 canary split
- Prepare two Deployments – one for v1 (stable) and one for v2 (canary). Annotate both for sidecar injection.
# v1 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-v1
labels:
app: myapp
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: v1
template:
metadata:
annotations:
traefikmesh.io/sidecar-injection: "enabled"
labels:
app: myapp
version: v1
spec:
containers:
- name: app
image: myapp:v1
ports:
- containerPort: 8080
---
# v2 Deployment (canary)
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-v2
labels:
app: myapp
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: myapp
version: v2
template:
metadata:
annotations:
traefikmesh.io/sidecar-injection: "enabled"
labels:
app: myapp
version: v2
spec:
containers:
- name: app
image: myapp:v2
ports:
- containerPort: 8080
- Expose a Service that selects both versions.
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
- Create the TrafficSplit resource.
apiVersion: traefikmesh.io/v1alpha1
kind: TrafficSplit
metadata:
name: myapp-split
namespace: default
spec:
service: myapp
backends:
- service: myapp-v1
weight: 90
- service: myapp-v2
weight: 10
- Apply and verify.
kubectl apply -f myapp-v1.yaml
kubectl apply -f myapp-v2.yaml
kubectl apply -f myapp-svc.yaml
kubectl apply -f myapp-split.yaml
# Check that the controller accepted the split
kubectl get traefikmeshsplit myapp-split -o yaml
# Look for status reflecting the weights (example output omitted)
To observe the traffic distribution, generate requests (e.g., a simple curl loop) and query Prometheus for the metrics exported by Traefik Mesh:
# Example traffic generator (run from a pod with curl)
while true; do curl -s http://myapp > /dev/null; sleep 0.1; done
# Prometheus query (approximate)
sum by (split) (rate(traefikmesh_requests_total[1m]))
You should see roughly a 9:1 ratio between the split="v1" and split="v2" time series.
Trade‑offs and limitations
- Sidecar dependency: If a pod lacks the injection annotation or runs outside the mesh, the TrafficSplit is ignored for that pod.
- Reconciliation delay: Weight changes are not instantaneous; allow a few seconds for Envoy to update its routing table.
- Granularity: TrafficSplit works at the service level; you cannot split traffic based on individual request attributes (e.g., headers) without layering additional policies.
Practical verification: after applying a new split, run kubectl get traefikmeshsplit <name> -o yaml and confirm the status.currentWeight fields match your spec. Then check the Prometheus metrics as shown above to see the observed ratio converge.
Actionable closing
If you need a lightweight canary mechanism and already operate Traefik Mesh, start by annotating your Deployments for sidecar injection, expose a Service that selects all versions, and define a TrafficSplit with the desired weights. Monitor the built‑in metrics to validate the split, and adjust weights incrementally. To roll back, simply reapply a TrafficSplit with the previous weight distribution (or set the canary weight to 0) and wait for the reconciliation period.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.