devops

Argo Rollouts Progressive Delivery: Canary and Blue-Green on Kubernetes

Argo Rollouts progressive delivery guide: canary and blue-green on Kubernetes with AnalysisTemplates, Prometheus-gated promotion, and instant rollback.

July 31, 2026·8 min read·
#kubernetes#devops#deployment#ci-cd#automation#sre

What Argo Rollouts Adds That a Deployment Can't

Argo Rollouts is a Kubernetes controller that replaces the standard Deployment with a Rollout resource capable of progressive delivery: canary releases that shift traffic in measured steps, blue-green cutovers with a preview environment, and — the part that actually matters — automated analysis that queries Prometheus during the rollout and aborts automatically if error rates climb. A stock Deployment gives you RollingUpdate, which only checks that new pods pass their probes. It has no idea whether the new version is returning 500s to real users; if the process starts and the readiness probe passes, Kubernetes calls it a success.

That gap is exactly what progressive delivery closes. This guide installs Argo Rollouts, builds a canary with Prometheus-gated promotion, shows the blue-green alternative, and covers the failure modes the docs gloss over.

One clarification that trips people up constantly: Argo Rollouts is not Argo CD. Argo CD syncs manifests from Git to the cluster; Rollouts controls how a workload updates once the manifest lands. They compose well — Argo CD applies the new image tag, Rollouts orchestrates the canary — but each works fine without the other. If you're setting up the GitOps side, start with the Argo CD production best practices guide.

Install the Controller and the kubectl Plugin

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f \
  https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# The kubectl plugin — you'll use this daily
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts

kubectl argo rollouts version

The plugin gives you a live TUI (get rollout --watch), plus promote, abort, and undo commands. There's also a web dashboard (kubectl argo rollouts dashboard), but the CLI is what you'll reach for during an incident.

A Canary Rollout With Real Traffic Steps

Here is a complete canary Rollout. It's deliberately close to a Deployment spec — the template and selector are identical; only apiVersion, kind, and the strategy block change:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: checkout-api
  namespace: production
spec:
  replicas: 10
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: checkout-api
  template:
    metadata:
      labels:
        app: checkout-api
    spec:
      containers:
        - name: checkout-api
          image: registry.example.com/checkout-api:v1.42.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              memory: 512Mi
  strategy:
    canary:
      canaryService: checkout-api-canary    # receives canary-weighted traffic
      stableService: checkout-api-stable    # receives the rest
      trafficRouting:
        nginx:
          stableIngress: checkout-api
      steps:
        - setWeight: 10
        - pause: { duration: 2m }
        - analysis:
            templates:
              - templateName: http-success-rate
        - setWeight: 30
        - pause: { duration: 5m }
        - setWeight: 60
        - pause: { duration: 5m }

How this executes: the controller creates a new ReplicaSet for v1.42.0, routes 10% of traffic to it via the NGINX ingress (it clones your ingress and sets the canary-weight annotation), waits two minutes, runs an analysis (next section), then walks up through 30% and 60% before fully promoting. At every step, kubectl argo rollouts abort rollout checkout-api snaps traffic back to stable in seconds — no image rebuild, no re-deploy.

Two operational notes:

  • Without trafficRouting, weights are approximate. Rollouts falls back to scaling pod counts (1 canary pod out of 10 ≈ 10%), and kube-proxy spreads traffic evenly across pods. Fine for coarse canaries, useless for 1% steps. With NGINX, Istio, ALB, or Traefik configured, weights are exact.
  • A pause step with no duration blocks forever until a human runs kubectl argo rollouts promote checkout-api. That's a feature — it's the simplest manual approval gate you can build — but forget it's there and your rollout sits at 10% overnight.

Your readiness probes still matter as the first line of defense: a canary pod that never becomes ready never receives traffic. Get those right first — the liveness, readiness, and startup probes guide covers the settings that interact badly with rollouts.

AnalysisTemplate: The Part That Makes It Progressive Delivery

Traffic steps without analysis are just a slow deployment. The AnalysisTemplate is where Rollouts earns its keep — it runs a Prometheus query on an interval and aborts the rollout automatically when the result crosses your threshold:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: http-success-rate
  namespace: production
spec:
  metrics:
    - name: success-rate
      interval: 1m
      count: 5                  # evaluate 5 times, 1 minute apart
      successCondition: result[0] >= 0.99
      failureLimit: 2           # 2 failed evaluations = abort
      provider:
        prometheus:
          address: http://prometheus.monitoring.svc.cluster.local:9090
          query: |
            sum(rate(http_requests_total{app="checkout-api",role="canary",status!~"5.."}[2m]))
            /
            sum(rate(http_requests_total{app="checkout-api",role="canary"}[2m]))

When the analysis fails, the controller aborts, scales the canary ReplicaSet to zero, and shifts all traffic back to stable — typically inside one interval of the regression appearing. Nobody gets paged to run a rollback.

The hard part is not the YAML; it's the query. Three rules from running this in production:

  1. Scope the query to canary pods only. If your metric mixes stable and canary traffic, a 10% canary throwing 100% errors still shows a 90% overall success rate and passes. Rollouts injects the labels — make sure your metrics pipeline preserves the ReplicaSet-level labels (or use the role: canary label on the canary Service, as above, if you scrape through the Service).
  2. Guard against empty results. During the first minute a canary may have served near-zero requests; rate() over no samples returns nothing and the analysis errors out (which counts toward failureLimit). Either set count/interval so evaluation starts after warm-up, or wrap the query so low traffic returns a passing value.
  3. Alert thresholds and analysis thresholds are different numbers. Your SLO alert might fire at 99.9% over an hour; a canary gate at 99% over 2 minutes is looser on purpose, because small windows are noisy. If you haven't defined SLIs yet, do that first — the SLI/SLO implementation guide with Prometheus and Grafana is the groundwork this gate stands on.

This assumes you have Prometheus scraping the app at all. If not, set up production Kubernetes monitoring with Prometheus and Grafana before bothering with automated analysis — a gate with no metrics behind it always passes.

Blue-Green: When You Can't Serve Two Versions at Once

Canary means both versions serve production traffic simultaneously. Some workloads can't tolerate that — schema-coupled backends, stateful consumers, anything where v1 and v2 writing at the same time corrupts data. Blue-green keeps one version live and stages the other behind a preview Service:

  strategy:
    blueGreen:
      activeService: checkout-api-active     # live traffic
      previewService: checkout-api-preview   # new version, test traffic only
      autoPromotionEnabled: false            # human flips the switch
      prePromotionAnalysis:
        templates:
          - templateName: smoke-test
      scaleDownDelaySeconds: 120             # keep old RS briefly for instant undo

The new ReplicaSet comes up at full replica count behind checkout-api-preview. You (or a CI job) hit the preview endpoint, the pre-promotion analysis runs, and on kubectl argo rollouts promote checkout-api the active Service selector flips to the new ReplicaSet — a cutover measured in milliseconds, with undo available for the scaleDownDelaySeconds window. The cost: you briefly run 2x replicas, which on large services is real money.

Choose canary when you want gradual risk exposure and can run versions side by side. Choose blue-green when the cutover must be atomic. Both beat the bare RollingUpdate you get from the zero-downtime GitHub Actions deployment setup — that pipeline is still how the image gets built and the manifest updated; Rollouts takes over from there.

The Commands You'll Actually Run

# Live view of a rollout stepping through canary weights
kubectl argo rollouts get rollout checkout-api --watch

# Trigger a rollout by updating the image (CI does this)
kubectl argo rollouts set image checkout-api checkout-api=registry.example.com/checkout-api:v1.43.0

# Approve a paused step / skip remaining steps
kubectl argo rollouts promote checkout-api
kubectl argo rollouts promote checkout-api --full

# Abort: shift all traffic back to stable NOW
kubectl argo rollouts abort checkout-api

# Roll back to the previous revision
kubectl argo rollouts undo checkout-api

One gotcha: after an abort, the Rollout reports Degraded even though stable is serving fine — that's by design, signalling the desired version isn't running. Fix forward with a new image, or undo to make the old version desired again.

Honest Limits

  • Migrating from Deployments is a real step. You either convert the manifest to kind: Rollout (one-time pod churn) or use the workloadRef field to point a Rollout at an existing Deployment and migrate gradually. Don't hand-edit live selectors.
  • HPA works, but target the Rollout (scaleTargetRef.kind: Rollout) — pointing the HPA at a leftover Deployment silently breaks scaling.
  • Analysis is only as good as your metrics. Teams install Rollouts, skip AnalysisTemplates because writing queries is hard, and end up with a slower deployment and no added safety. If you're not going to gate on metrics, a plain Deployment with good probes is less machinery for the same result.
  • It's one more controller to run. Budget for upgrading it, monitoring it, and understanding its CRDs. Where it pays off is measurable: canary gates directly cut change failure rate and shrink recovery time — two of the DORA metrics you can track with GitHub Actions and Prometheus.

Start with one high-traffic, stateless service. Get a 10% canary with a single success-rate analysis working end to end, let it catch one bad release for you, and the rest of the org will ask for it by name.

#kubernetes#devops#deployment#ci-cd#automation#sre
D
DevToCashAuthor

Senior DevOps/SRE Engineer · 10+ years · Professional Trader (IDX, Crypto, US Equities)

I write about real infrastructure patterns and trading strategies I use in production and in live markets. No courses, no affiliate hype — just documentation of what actually works.

More about me →