Traefik Mesh Canary Releases: Choosing and Validating SMI TrafficSplit
Learn how to implement weighted canary releases in Traefik Mesh using SMI TrafficSplit, including constraints, alternatives, and validation steps.
28 Jan 2026, 17:13 UTC

The decision: weighted east-west canary without vendor lock-in
Shifting a percentage of live traffic from one service version to another inside a Kubernetes mesh usually requires either a vendor-specific custom resource or adopting a heavier external service mesh. Traefik Mesh offers a narrower path: the SMI TrafficSplit resource. It implements the Service Mesh Interface specification natively, so you can perform percentage-based canary releases between service versions using a standard Kubernetes custom resource rather than a Traefik-specific API. The trade-off is that TrafficSplit controls only east-west traffic inside the mesh; it does not replace ingress canary routing or advanced resilience policies.
Constraints and prerequisites
Before choosing TrafficSplit, confirm these constraints:
- SMI CRDs must be registered. Traefik Mesh reads TrafficSplit objects, but the custom resource definitions must exist in the cluster first.
- Cluster-scoped controller. The Traefik Mesh controller must run with cluster scope to observe TrafficSplit resources across namespaces.
- Same mesh namespace. The root service and all backend services must reside in the same namespace that is part of the mesh.
- East-west only. TrafficSplit affects pod-to-pod or service-to-service traffic inside the mesh. It does not shape external ingress traffic.
- Root service selector. The root service referenced by the TrafficSplit must have no selector, or must be a headless service whose endpoints are managed exclusively by the split. A selector that points directly at pods bypasses the split and can cause 503 errors.
Because SMI specification versions differ across releases, verify which TrafficSplit API version your Traefik Mesh distribution supports before applying manifests. Some versions use v1alpha1, others v1alpha2.
Comparing supported options
| Option | Traffic scope | Routing control | Operational overhead | Best fit |
|---|---|---|---|---|
| SMI TrafficSplit | East-west (mesh internal) | Weighted percentage only | Low | Canary between in-mesh services |
| Traefik IngressRoute canary | North-south (external) | Weighted, header, cookie | Low | Edge or ingress canary |
| External mesh (Linkerd, Istio) | East-west and more | Rich: headers, retries, circuit breaking | High | Full-featured mesh requirements |
| Manual replica scaling | N/A | None (capacity only) | Low | Blue-green without routing |
Trade-offs to weigh
TrafficSplit gives you precise percentage control and fast rollback: editing weights and reapplying the resource shifts traffic immediately without redeploying pods. However, it lacks header- or cookie-based routing, so you cannot direct a specific user segment to the new version. It also does not provide retries or circuit breaking at the mesh level; those concerns remain with the application or an external mesh. Adding a second mesh alongside Traefik Mesh increases operational complexity, so if you already need advanced traffic policies, an external mesh may be the better long-term decision.
Implementation: a weighted 90/10 split
The following example assumes Traefik Mesh is installed and the SMI CRDs are present. Replace <namespace>, <app-name>, and image tags with your values.
1. Verify the TrafficSplit CRD
Run this in your terminal with kubectl configured for the target cluster. You need read access to API resources at the cluster level.
kubectl api-resources | grep trafficsplit
Expected check: The output lists trafficsplits with an API group such as split.smi-spec.io. If nothing appears, install the SMI operator or CRDs before proceeding.
2. Deploy versioned services and a selector-less root service
Create two Deployments and matching Services for v1 and v2, plus a root Service with no selector. The root Service name is what clients call; the split routes its traffic to the backends.
apiVersion: v1
kind: Service
metadata:
name: <app-name>
namespace: <namespace>
spec:
ports:
- port: 80
targetPort: 8080
# No selector: endpoints are supplied by the TrafficSplit controller
---
apiVersion: v1
kind: Service
metadata:
name: <app-name>-v1
namespace: <namespace>
spec:
selector:
app: <app-name>
version: v1
ports:
- port: 80
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: <app-name>-v2
namespace: <namespace>
spec:
selector:
app: <app-name>
version: v2
ports:
- port: 80
targetPort: 8080
Apply with kubectl apply -f services.yaml. You need create/update permissions for Services and Deployments in <namespace>.
3. Create the TrafficSplit
Adjust the apiVersion to match the version confirmed in step 1.
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: <app-name>-canary
namespace: <namespace>
spec:
service: <app-name>
backends:
- service: <app-name>-v1
weight: 90
- service: <app-name>-v2
weight: 10
Apply with kubectl apply -f trafficsplit.yaml. You need create/update permissions for TrafficSplit resources in <namespace>. The Traefik Mesh controller must have cluster scope to reconcile the split.
Validation and diagnostics
Confirm the resource exists and the controller has processed it.
kubectl get trafficsplit -n <namespace>
Inspect the controller logs for reconciliation events. Replace <controller-name> with the actual Traefik Mesh controller deployment name in the traefik-mesh namespace.
kubectl logs -n traefik-mesh deployment/<controller-name>
To observe the distribution, exec or run a temporary pod inside the mesh and call the root service repeatedly. The following example uses a throwaway pod; you need permission to create pods in <namespace>.
kubectl run -n <namespace> curl-test --rm -i --restart=Never --image=curlimages/curl -- sh -c 'for i in $(seq 1 20); do curl -s http://<app-name>.<namespace>.svc.cluster.local/version; done'
Expected check: Over 20 requests, roughly 18 should reach v1 and 2 should reach v2, subject to connection reuse and keep-alive behavior. If you receive 503 responses, verify that the root Service has no selector and that both backend Services have healthy endpoints.
If Prometheus is configured for Traefik Mesh, check request counters for the root and backend services to confirm the weighted distribution over time.
Rollback
Because applying a TrafficSplit changes live routing state, rollback is a state change. Reapply the manifest with weights restored to 100/0:
kubectl apply -f trafficsplit.yaml # weight: 100 for v1, 0 for v2
No pod redeploy is required. Existing keep-alive connections may continue to reach the old backend briefly until they close.
Limitations and lifecycle review
- API version drift. SMI spec versions vary. Verify the exact
apiVersionsupported by your Traefik Mesh release before applying manifests. - Misconfiguration risk. A root Service with a selector, or missing backend endpoints, can produce 503 errors. Validate Service definitions before creating the split.
- Feature ceiling. TrafficSplit supports weighted routing only. It does not offer header/cookie matching, retries, or circuit breaking.
- Project status. Traefik Mesh is described as maintenance or legacy in some contexts. Confirm the current lifecycle status and support commitments before adopting it for new production clusters.
A practical final check is to run the curl loop above after every weight change and compare the observed ratio against the declared weights. If the distribution does not converge, inspect the controller logs and Service endpoints before increasing traffic to the new version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.