I wanted to test a new version of my app on twenty percent of the traffic. I switched my Azure Container App to multiple revisions, deployed the new version and sent 200 requests. The new version answered all 200.
This explainer shows how Container Apps decides which revision answers each request, how to get a real 80/20 split, what that split means for a single user, and what an old revision at zero percent was still doing. Everything below comes from a real run in my own Azure subscription.
Key points
- Single revision mode can't split. It's the default: one active revision at a time, and a deploy is the release. Asking for weights fails with "configured for single revision".
- Multiple revision mode doesn't hold a new version back. The default traffic rule is
latestRevision: trueat 100 %. It points at whatever is newest, so every deploy takes all the traffic once it's ready. Name the revisions in the weights before you deploy, and a new revision starts at zero. - 80/20 is per request, not per user. 1,000 requests split 806 / 194. One client on one keep-alive connection saw both versions. Sticky sessions only exist in single revision mode. For testers, a label gives one revision its own address.
- Every active revision runs its own replicas. At 30 requests a second, one revision ran five replicas and two revisions at 50/50 ran two plus three. A revision at zero percent kept four replicas for four and a half minutes and then its minimum of one, until I deactivated it.
One revision at a time
Every change to the container (a new image, a new environment variable, a new scale rule: anything in the app's template) builds a new revision, a snapshot of the app that never changes. Each revision runs as replicas, copies of your container. Changes to the app's configuration (secrets, ingress, the traffic rules, labels) don't build a revision.
By default, a Container App runs in single revision mode: one revision is active at a time. When I deployed version two, v2 took all the traffic as soon as it was ready, and a little later v1 was deactivated on its own. In this mode, a deploy is the release.
So I asked for a split anyway, 80 % to v1 and 20 % to v2:
> az containerapp ingress traffic set -n shop -g rg-demo --revision-weight shop--v1=80 shop--v2=20ERROR: Containerapp 'shop' is configured for single revision. Set revision mode to multiple in order to set ingress traffic.
One active revision means there's nothing to split.
The newest takes it all
Multiple revision mode lets several revisions be active at once, and you decide how they share the traffic. One command switches the mode:
az containerapp revision set-mode -n shop -g rg-demo --mode multiple
Then I deployed v3, my canary, the same way as before. v2 was still active next to it. 200 requests later, v3 had answered all 200. The traffic rules explained why:
> az containerapp ingress traffic show -n shop -g rg-demo[{"latestRevision": true,"weight": 100}]
That's the default. The rule doesn't name a revision; it points at whatever is newest. Multiple mode only kept v2 running next to the new one.
The fix is to name the revisions. Pin the weights by revision name first, then deploy, then move the weights yourself:
az containerapp ingress traffic set -n shop -g rg-demo \--revision-weight shop--v2=100az containerapp update -n shop -g rg-demo \--set-env-vars APP_VERSION=v3 --revision-suffix v3az containerapp ingress traffic set -n shop -g rg-demo \--revision-weight shop--v2=80 shop--v3=20
Later in my run, with the weights still naming v2 and v3, I deployed v4: it became active with one replica and got 0 % of the traffic.
| Single revision mode | Multiple revision mode | |
|---|---|---|
| Active revisions | one | as many as you keep active |
| A new deploy | takes all traffic once ready; the old one is deactivated | default rule: the latest takes 100 % once ready; named weights: starts at 0 % |
| Traffic splitting | refused | by weight (revision name or label) |
| Sticky sessions | available | not available |
| Old revisions | deactivated for you | keep running until you deactivate them |
The docs list a third mode, deployment labels (preview), where labels like stage and prod get new revisions assigned and old ones shut down when no label points at them.
Twenty percent of what?
With the weights at 80 and 20, I sent 1,000 requests, each on a new connection: 806 for v2, 194 for v3. Close to 80/20, not exact: each request is sent on its own, by chance, following the weights.
Then one user: one client, one keep-alive connection, ten requests in a row. It saw v2 v2 v2 v3 v2 v3 v2 v2 v2 v2. The split is per request, not per user. Session affinity (sticky sessions) is only supported in single revision mode, so if a user has to stay on one version, the weights alone won't do it.
For testers, there's a label. A label gives one revision its own address, whatever the weights say:
az containerapp revision label add -n shop -g rg-demo \--label canary --revision shop--v3
The label's address is the app name, three dashes, the label, then the domain of the Container Apps environment: https://shop---canary.<env-domain>.westeurope.azurecontainerapps.io/. It answered v3 every time, 100 calls out of 100, while v3 still had 20 % of the normal traffic. A label can sit on a revision at 0 %, so you can test before you give it any real traffic. Real users follow the weights. Testers use the label.
Every gate has its own crew
Every active revision has its own replicas, and each one scales on its own share of the requests. My HTTP scale rule asked for about one replica per ten requests a second (--scale-rule-http-concurrency 10).
| 30 requests/s for 3 minutes | v2 | v3 | Total |
|---|---|---|---|
| v3 at 100 % | 4 (left over, 0 % traffic) | 5 | 9 |
| 50 / 50 (after the scale-in) | 2 | 3 | 5 |
Split fifty-fifty, the same load used five replicas, the same as one revision alone. The split itself didn't cost extra replicas in my run; the module warns it can, because each revision keeps its own minimum and rounds up on its own. What adds up is every revision you leave active.
The old revision at zero percent
When I moved v2 to 0 %, it still had four replicas from the thousand requests before. It kept them for four and a half minutes, then two, then, eight minutes after it lost its traffic, one. And that one stays: it's the minimum replica count I gave the app, and every active revision keeps it. Zero percent of the traffic is not zero replicas.
At the end I deactivated the revisions I didn't need:
az containerapp revision deactivate -n shop -g rg-demo --revision shop--v4
An inactive revision runs no replicas and costs nothing (Container Apps keeps up to 100 of them, so you can activate one again).
Move the weight to zero before you deactivate. In my run, revision deactivate also succeeded on a revision that still had 50 % of the weight. The requests went to the other revision right away (200 of 200, also two minutes later), but the traffic list kept showing the inactive revision at 50 % until I set the weights again.
What happened when I ran it
Tested on October 21, 2026 in my own Azure subscription (westeurope, Azure CLI 2.88.0); everything was deleted afterwards. The app is my own tiny Python API that answers {"version": "v3", ...}; a new version is a new value of APP_VERSION plus a revision suffix. split.py, also my own, counted which version answered, played one user on one connection, sent load at a fixed rate and polled the replicas of every active revision. The environment took 17.5 minutes to create, each update about 20 seconds plus 15 seconds until the new revision was running.
After v3 in multiple mode, nothing pinned:
> az containerapp revision list -n shop -g rg-demo -o tableActive Replicas TrafficWeight NameTrue 1 0 shop--v2True 1 100 shop--v3> python split.py --app shop --rg rg-demo send 200200 requests to the app URL in 4 sv3 200 100.0 %
The 80/20 split, 1,000 requests and one user:
> python split.py --app shop --rg rg-demo send 10001000 requests to the app URL in 18 sv2 806 80.6 %v3 194 19.4 %> python split.py --app shop --rg rg-demo user 10one client, one connection, 10 requests:v2 v2 v2 v3 v2 v3 v2 v2 v2 v2v2: 8, v3: 2
The label:
> curl.exe -s https://shop---canary.<env-domain>.westeurope.azurecontainerapps.io/{"version": "v3", "replica": "?"}
v4 deployed while the weights named v2 and v3:
Active Replicas TrafficWeight NameTrue 2 50 shop--v2True 3 50 shop--v3True 1 0 shop--v4
v2 at 0 % after the load test (my tool polled every 15 seconds):
2 s shop--v2 4 repl. (0 %) · shop--v3 5 repl. (100 %)53 s shop--v2 2 repl. (0 %) · shop--v3 5 repl. (100 %)277 s shop--v2 1 repl. (0 %) · shop--v3 4 repl. (100 %)517 s shop--v2 1 repl. (0 %) · shop--v3 1 repl. (100 %)
The deactivation with weight left on it:
> az containerapp revision list -n shop -g rg-demo --all -o tableActive Replicas TrafficWeight NameTrue 6 50 shop--v3False 0 50 shop--v4
And one detail: right after containerapp create with a revision suffix, the list showed an extra, automatically named revision (shop--ttolvpb, active, one replica, 0 %) next to shop--v1; by the next deploy it was inactive.





