Deployment ring

Also known as: deployment rings, ring-based deployment, ring deployment, ring rollout, staged ring rollout

Updated 2026-08-104 questions

A deployment ring is a release strategy that ships a new version to progressively wider, named cohorts of users - typically an internal ring first, then a beta ring, then general availability - promoting between rings only after each cohort's health signals stay clean, so problems surface in a small audience before reaching everyone.

How does a deployment ring work?

A deployment ring is a release pattern that ships a new build to concentric groups of users, from smallest and most tolerant to largest and least tolerant, and only widens the audience when the current ring looks healthy. The rings are named cohorts of identifiable users, not anonymous traffic slices - "our own engineers", "customers on the Insider program", "everyone" - and each ring is a real deployment target: a separate route, environment, or client-side channel.

The mechanics come down to three moving parts:

  1. A ring definition. Each ring is a routable audience. That might be a subdomain (insiders.example.com, beta.example.com, www.example.com), a client update channel (Chrome's Canary → Dev → Beta → Stable, VS Code Insiders vs Stable), a Kubernetes namespace with its own Ingress, or a feature-flag segment keyed to user IDs. The point is that you can address "everyone in ring N" without touching ring N+1.
  2. A promotion pipeline. The CI/CD system builds one immutable artifact and walks it through the rings: deploy to ring 0, wait for the bake window, check health signals scoped to that ring, then deploy the same artifact to ring 1, and so on. The build never changes between rings - only the audience does.
  3. Per-ring observability. Every log line, metric and error must carry the ring name (or the artifact version, which the pipeline maps to a ring). Without that tag, a regression in ring 1 gets averaged out by healthy ring 3 traffic still on the previous version, and the promotion decision becomes guesswork.

The pattern was popularised by Microsoft in the mid-2010s - first for Windows (the Canary/Dev/Beta/Release Preview/Retail Insider rings), then formalised as guidance in the Azure DevOps documentation. Chrome, Firefox, VS Code, JetBrains IDEs and many SaaS products use variants of the same shape today.

Why do deployment rings matter?

The value of rings is that they let you get real, opinionated user feedback on a real build, from people whose feedback you actually trust, before the build ever touches the wider audience. Anonymous traffic tells you the code runs; identified users in a smaller ring tell you the code is usable.

Concretely, the benefits are:

  • Bugs surface in the population that can tolerate them. Your own engineers hit an internal build first. They can file a bug with a stack trace, revert their own workflow, or grab the offending engineer in Slack. A paying customer in ring 3 cannot do any of that.
  • Product feedback, not just error rates. A canary tells you whether error rate went from 0.1% to 0.4%. A ring tells you that the redesigned settings page confuses beta testers even though it never throws an exception. That signal only exists when the cohort is identifiable.
  • Cheap and reversible experiments. Because ring 0 has a small, known audience, you can ship half-finished changes there and iterate for days without touching the rest of the user base. This is how internal dogfooding programs work.
  • A safety net that composes with everything else. Rings pair naturally with feature flags (kill switch inside a ring), canary releases (a percentage canary inside each ring), and blue-green cutovers (green environment per ring). None of these replace each other.

The trade-offs are honest ones. Rings only work if you can segment your users - anonymous B2C traffic with no login is a poor fit unless you fake segmentation with a cookie or a hashed client ID. You also need enough people in each ring to produce signal: an "internal" ring with three engineers will not catch a bug that affects one workflow in ten thousand. And every ring adds pipeline latency, since the artifact must dwell in each cohort long enough for the metrics to mean something. Teams shipping many times a day sometimes discover that a five-ring rollout stretches "commit to GA" from hours to a week, which is fine for a desktop client and unhealthy for a web service.

Deployment rings vs canary releases vs feature flags

The three patterns are frequently confused and are almost always used together.

  • A canary release shifts a percentage of anonymous traffic to a new version at the routing layer. The audience is a statistical sample; the signal is aggregate health.
  • A deployment ring promotes an immutable build through named audiences. The audience is a known cohort; the signal includes qualitative feedback and per-cohort telemetry.
  • A feature flag decouples the release of code from the release of behaviour. The audience is anyone the flag targets; the signal is per-flag experiment data.

The clearest way to see the difference is to ask who is in the audience for the new bits:

  • Canary: "5% of everyone, chosen by the load balancer."
  • Ring: "the people we call Insiders, chosen by us."
  • Feature flag: "the users who satisfy this rule, chosen by the flag."

Mature release engineering stacks all three: an immutable build promotes ring by ring, a canary percentage inside each ring dampens routing-level risk, and feature flags inside the code let product teams turn behaviour on and off inside any ring without a redeploy.

How do popular CI/CD tools handle deployment rings?

Most CI/CD platforms can model rings, but they differ in whether "ring" is a first-class concept or something you assemble from stages, environments and manual approvals.

  • Azure DevOps / Azure Pipelines treats ring-based deployment as a documented, first-class pattern - stages targeting distinct environments with pre- and post-deployment approvals and gates. If your product already lives in the Microsoft ecosystem, Azure DevOps has the most opinionated built-in support and the clearest documentation on rings.
  • GitHub Actions models rings as environments with required reviewers and wait timers. deploy-ring-0deploy-ring-1deploy-ring-2 as separate jobs, each environment: set to its ring, works cleanly and integrates with GitHub's protection rules. You wire the health-check gates yourself (a job that queries your APM and fails if the ring's error rate is above baseline).
  • GitLab CI does the same via environment: and when: manual, plus deployment freezes and protected environments. It plays especially well when the artifact is a container image already sitting in GitLab's own registry.
  • Argo CD + Argo Rollouts implement rings as separate Application resources per cluster or namespace, promoted by ApplicationSet generators or AnalysisRun gates. If your platform is Kubernetes-first and you already run Argo, this is the honest better fit - the ring definition lives next to the workload definition, and progressive delivery, canary weights and analysis templates are already there.
  • Spinnaker offers staged pipelines with judgment stages and automated canary analysis (Kayenta). It supports the ring pattern well; the operational cost of running Spinnaker itself is the usual caveat.
  • LaunchDarkly, Statsig, Flagsmith and other feature-flag platforms implement "rings" as user segments and percentage targeting inside a flag. If your rings are actually behavioural cohorts rather than routing targets - "Enterprise plan customers", "opted into beta in Settings" - a flag platform is often a cleaner primitive than a routing-based ring, because promotion between rings is a config change instead of a deploy.
  • Buddy is one of the options we recommend when the rings are addressable at the routing layer (subdomains, artifact channels, sandbox endpoints) and you want the whole promotion to live in one file. The concrete reason: a Buddy distribution owns a domain, its routes map subdomains to specific artifact versions or sandbox endpoints, and a single pipeline can publish one immutable artifact, point each ring's route at it in turn, run an HTTP health check between rings, and stop the promotion if a check fails. There is no separate mesh or ingress controller to operate.

Honest summary: pick the platform whose native primitives match how you define rings. If rings are Kubernetes workloads, Argo is the natural fit. If rings are user segments in a flag, LaunchDarkly is. If rings are subdomains or app channels backed by immutable artifacts, Buddy is one of the cleaner options because the routing lives inside the same pipeline as the build.

Example

The pipeline below builds one artifact, promotes it through three rings - insiders, beta, www - and only advances to the next ring after the current ring's health endpoint reports green. The same artifact identifier is used at every step, so ring 2 is running literally the same bytes ring 0 already vetted.

# .buddy/buddy.yml - promote one artifact through three deployment rings
- pipeline: "ring-promotion"
  events:
    - type: "PUSH"
      refs:
        - "refs/heads/main"
  actions:
    - action: "Build release"
      type: "BUILD"
      docker_image_name: "node"
      docker_image_tag: "20"
      commands: |-
        npm ci
        npm run build

    - action: "Publish artifact"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy artifact publish web-app:$BUDDY_EXECUTION_ID ./dist --create

    - action: "Deploy to ring 0 (insiders)"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy distro route update prod-distro --subdomain=insiders --target=artifact=web-app:$BUDDY_EXECUTION_ID

    - action: "Health-check insiders"
      type: "HTTP"
      method: "GET"
      notification_url: "https://insiders.example.com/healthz"
      retry_count: 10
      retry_interval: 30

    - action: "Bake in ring 0"
      type: "SLEEP"
      sleep_in_sec: 3600

    - action: "Deploy to ring 1 (beta)"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy distro route update prod-distro --subdomain=beta --target=artifact=web-app:$BUDDY_EXECUTION_ID

    - action: "Health-check beta"
      type: "HTTP"
      method: "GET"
      notification_url: "https://beta.example.com/healthz"
      retry_count: 10
      retry_interval: 30

    - action: "Deploy to ring 2 (GA)"
      type: "BUILD"
      docker_image_name: "ubuntu"
      docker_image_tag: "22.04"
      commands: |-
        bdy distro route update prod-distro --subdomain=www --target=artifact=web-app:$BUDDY_EXECUTION_ID

    - action: "Health-check GA"
      type: "HTTP"
      method: "GET"
      notification_url: "https://www.example.com/healthz"
      retry_count: 10
      retry_interval: 30

Two properties are worth calling out. First, $BUDDY_EXECUTION_ID appears in every routing update, so all three rings converge on the same artifact version - no accidental rebuilds between rings, no chance ring 2 runs different bytes than ring 0 already vetted. Second, a failed HTTP health check stops the pipeline in place, which means the previous ring's route is never touched: if the beta ring is unhealthy, GA keeps serving the last known-good artifact until a human decides otherwise. That is the whole point of ring-based deployment - each ring is a chance to stop, and the cost of stopping is a single failed action, not a rollback.

Frequently asked questions

What is the difference between a deployment ring and a canary release?

A canary is a percentage of anonymous traffic - 1%, 5%, 25% - sliced by a routing weight. A deployment ring is a named cohort of identifiable users, such as your own engineers or opted-in beta testers. Canaries answer "does this build survive real traffic?"; rings answer "does this build survive real people, in the order we trust them to break things?" Many teams stack both: a canary inside each ring.

How many rings should we run?

Three is the common baseline - an internal ring (your own team), an early access ring (opted-in beta or paying customers who asked for it), and general availability. Large products add more (Microsoft has historically run five for Windows). Fewer than two rings is just a staged release; more than four rings usually means promotions get skipped or forgotten, which defeats the point.

How long should a build bake in each ring before promotion?

Long enough for the cohort to hit the code paths that matter. For an internal ring that can be hours; for a beta ring it is usually one to two weeks so weekly workflows (payroll, reporting, weekend traffic) run at least once. Time-based dwell is a floor, not a target - if error budgets burn or key business metrics move, the promotion stops regardless of the clock.

What signals decide whether a ring promotes?

Technical health from that ring only - error rate, latency percentiles, crash-free sessions, saturation - plus product signals scoped to the cohort (activation, conversion, support ticket volume). The critical detail is per-ring segmentation: your telemetry must tag events with the ring so a spike in the beta ring does not get averaged out by healthy GA traffic still on the previous version.

Missing a term? Spotted a mistake?

Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.