Argo Rollouts — Progressive Delivery in Kubernetes
Argo Rollouts extends Kubernetes Deployments with canary and blue-green strategies, automated analysis, and traffic management integration.
Why Not Plain Kubernetes Rolling Updates?
| K8s RollingUpdate | Argo Rollouts | |
|---|---|---|
| Traffic control | None — replicas = traffic | Precise % split (1% → 5% → 50% → 100%) |
| Automated analysis | No | Yes — query Prometheus/Datadog, pause on errors |
| Pause/resume | No | Yes — manual gate or metric gate |
| Blue-green | No | Yes — full traffic switch |
| Rollback trigger | Manual only | Automatic on metric degradation |
| Preview URL | No | Yes — separate Service for canary |
Architecture
graph TD
ROLLOUT["Rollout CRD<br/>(replaces Deployment)"] --> RS_STABLE["ReplicaSet: stable<br/>(current version)"]
ROLLOUT --> RS_CANARY["ReplicaSet: canary<br/>(new version)"]
ROLLOUT --> ANAL["AnalysisRun<br/>(queries Prometheus/Datadog)"]
ROLLOUT --> SVC_STABLE["Service: stable<br/>(100% --> reduces as canary grows)"]
ROLLOUT --> SVC_CANARY["Service: canary<br/>(0% --> grows)"]
SVC_STABLE --> LB["Ingress / AWS ALB<br/>weighted routing"]
SVC_CANARY --> LB
CTRL["Argo Rollouts Controller<br/>(watches Rollout CRDs)"] --> ROLLOUT
Install
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
-f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
# kubectl plugin for managing rollouts
brew install argoproj/tap/kubectl-argo-rollouts
Canary Rollout
Basic canary
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 10
selector:
matchLabels:
app: my-app
template: # same as Deployment pod template
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:v2
ports:
- containerPort: 8080
strategy:
canary:
# Canary traffic goes to this service (separate from stable)
canaryService: my-app-canary
stableService: my-app-stable
steps:
- setWeight: 5 # send 5% traffic to canary
- pause: {duration: 5m} # wait 5 min
- setWeight: 20
- pause: {} # pause indefinitely — manual promotion required
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 100 # full rollout
With automated Prometheus analysis
strategy:
canary:
canaryService: my-app-canary
stableService: my-app-stable
steps:
- setWeight: 10
- analysis:
templates:
- templateName: success-rate # ← AnalysisTemplate defined below
args:
- name: service-name
value: my-app-canary
- pause: {duration: 5m}
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 100
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95 # 95% success rate required
failureLimit: 3 # 3 consecutive failures = abort rollout
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{
service="{{args.service-name}}",
status!~"5.."
}[2m])) /
sum(rate(http_requests_total{
service="{{args.service-name}}"
}[2m]))
What happens on failure: If the success rate drops below 95% for 3 consecutive checks, the rollout automatically aborts — scales canary to 0, promotes nothing, and the stable version continues serving 100%.
ALB weighted routing (AWS)
For precise traffic splitting without replica-count tricks:
strategy:
canary:
canaryService: my-app-canary
stableService: my-app-stable
trafficRouting:
alb:
ingress: my-app-ingress # the ALB Ingress resource
servicePort: 8080
steps:
- setWeight: 1 # 1% canary — impossible with just replicas
- pause: {duration: 10m}
- setWeight: 10
- pause: {}
- setWeight: 100
# The Ingress — Argo Rollouts manages the weights automatically
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
kubernetes.io/ingress.class: alb
spec:
rules:
- http:
paths:
- path: /
backend:
service:
name: my-app-stable # Rollouts controller updates weights on ALB
port:
number: 8080
Blue-Green Rollout
flowchart LR
subgraph Before
ACTIVE["active Service --> v1 pods<br/>(100% traffic)"]
PREVIEW["preview Service --> v2 pods<br/>(0% traffic, internal only)"]
end
subgraph After["After promotion"]
ACTIVE2["active Service --> v2 pods<br/>(100% traffic)"]
OLD["v1 pods kept 30s<br/>then scaled down"]
end
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app-bg
spec:
replicas: 5
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:v2
strategy:
blueGreen:
activeService: my-app-active # production traffic
previewService: my-app-preview # test traffic (internal, QA)
autoPromotionEnabled: false # require manual promotion
# autoPromotionSeconds: 60 # or auto-promote after 60s
prePromotionAnalysis: # run analysis before switching traffic
templates:
- templateName: success-rate
args:
- name: service-name
value: my-app-preview
scaleDownDelaySeconds: 30 # keep old (blue) pods 30s after switch
Promotion:
# Manual promotion — switch active service to new version
kubectl argo rollouts promote my-app-bg
# Or via ArgoCD UI / CLI
argocd app sync my-app # if using ArgoCD to manage the Rollout
kubectl Commands
# Watch rollout progress (live)
kubectl argo rollouts get rollout my-app --watch
# Promote to next step (unpause a manual pause)
kubectl argo rollouts promote my-app
# Abort rollout (scale canary to 0, stable remains)
kubectl argo rollouts abort my-app
# Manually retry after abort
kubectl argo rollouts retry rollout my-app
# Set image (triggers new rollout)
kubectl argo rollouts set image my-app app=my-app:v3
# Pause a running rollout
kubectl argo rollouts pause my-app
# View analysis run results
kubectl argo rollouts get rollout my-app -o json | jq '.status.currentStepAnalysisRunStatus'
ArgoCD + Argo Rollouts Integration
ArgoCD manages the Rollout CRD the same way it manages Deployment. Install the ArgoCD Rollouts extension so ArgoCD understands Rollout health:
# ArgoCD needs a custom health check for Rollout objects (Lua script in argocd-cm)
# Most setups use the official Rollouts plugin:
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj-labs/argocd-extensions/main/examples/rollout/rollout-extension.yaml
# argocd-cm ConfigMap — custom health check for Rollout
resource.customizations.health.argoproj.io_Rollout: |
hs = {}
if obj.status ~= nil then
if obj.status.phase == "Healthy" then
hs.status = "Healthy"
elseif obj.status.phase == "Paused" then
hs.status = "Suspended"
hs.message = "Rollout is paused"
elseif obj.status.phase == "Degraded" then
hs.status = "Degraded"
hs.message = obj.status.message
else
hs.status = "Progressing"
end
end
return hs
GitOps flow with Argo Rollouts:
- Developer bumps image tag in git
- ArgoCD syncs → creates new
Rolloutrevision - Argo Rollouts controller starts canary steps
- Analysis queries Prometheus automatically
- On success: full promotion. On failure: auto-rollback to previous revision in git.
Canary vs Blue-Green Decision
| Canary | Blue-Green | |
|---|---|---|
| Traffic split | Gradual (1% → 100%) | Instant switch |
| Resource cost | Low (only a few canary pods) | 2× during transition |
| Rollback speed | Fast (shift traffic back) | Instant (flip service) |
| Best for | Stateless services, high traffic | Services needing zero-downtime switch, stateful |
| DB migrations | Risky — both versions run simultaneously | Safer — preview env for testing |
| Real user validation | Yes — real traffic on canary | No — preview is internal only |