Introduction to Flagger and Canary Deployments
Flagger is an open-source progressive delivery operator for Kubernetes, originally created by Weaveworks and now part of the Flux ecosystem. It automates canary deployments by gradually shifting traffic to a new version of your application while monitoring key metrics like request latency, error rates, and success rates. If the new version meets your defined quality gates, Flagger completes the rollout. If it fails, Flagger automatically rolls back to the stable version, minimizing user impact.
What Exactly Is a Canary Deployment?
A canary deployment is a technique where a new version of an application (the "canary") is deployed alongside the existing stable version. A small percentage of production traffic is routed to the canary, allowing you to test real-world behavior with a fraction of users. If the canary performs well, traffic increases incrementally until 100% of traffic reaches the new version. If problems arise, traffic is shifted back to the stable version immediately. Flagger automates this entire process using metrics from your service mesh or ingress controller.
Why Flagger Matters for Modern DevOps
Manual canary deployments require constant human monitoring, manual traffic shifting, and subjective decisions about when to promote or roll back. Flagger replaces this fragile process with automated, metric-driven decisions. Key benefits include:
- Reduced deployment risk: Bad changes affect only a tiny fraction of users before automatic rollback
- Continuous metric validation: Every step of the canary is backed by real latency, error, and throughput data
- Integration with GitOps: Flagger works seamlessly with Flux for declarative, version-controlled delivery pipelines
- Multi-mesh support: Works with Istio, Linkerd, NGINX, Contour, Traefik, and more
- Customizable analysis: Define your own metrics, thresholds, and analysis windows
How Flagger Works: Architecture Deep Dive
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Flagger operates as a Kubernetes controller. It watches for changes to a custom resource called a Canary resource. When a new container image is detected in the primary deployment, Flagger initiates the following workflow:
- Detection: Flagger monitors the primary deployment's container image. A change triggers the canary process
- Scaling: Flagger scales the canary deployment to zero, then adjusts replicas based on your configuration
- Traffic shifting: Using the service mesh or ingress controller, Flagger routes a defined percentage of traffic to the canary
- Metric analysis: Flagger queries Prometheus, Datadog, or other metric providers to evaluate the canary's health
- Promotion or rollback: If metrics pass, traffic increases step by step. If they fail, traffic returns to the primary deployment
Key Custom Resources
Flagger introduces three essential custom resource definitions (CRDs):
- Canary: Defines the deployment strategy, metrics, thresholds, and analysis parameters
- MetricTemplate: Reusable metric definitions that can be shared across multiple Canary resources
- AlertProvider: Integration points for Slack, MS Teams, Discord, or other notification channels
Prerequisites for This Tutorial
Before you begin, ensure you have the following in place:
- A Kubernetes cluster (version 1.16+) with kubectl configured
- A service mesh installed (Istio, Linkerd, or an ingress controller like NGINX)
- Prometheus deployed and scraping metrics from your mesh/ingress
- Helm v3 installed for Flagger installation
- A sample application to use as the canary target
Step-by-Step Implementation Guide
Step 1: Install Prometheus (If Not Already Present)
Flagger relies on Prometheus metrics by default. Install the Prometheus community stack with Helm:
# Add the Prometheus community Helm repository
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# Install kube-prometheus-stack in the monitoring namespace
helm upgrade --install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--wait
# Verify Prometheus is running
kubectl get pods -n monitoring
kubectl get svc -n monitoring
Step 2: Choose Your Service Mesh or Ingress Controller
For this guide, we'll use Istio, but Flagger supports many providers. Install Istio with the following:
# Download Istio
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.19.0 sh -
cd istio-1.19.0
export PATH=$PWD/bin:$PATH
# Install Istio with default profile
istioctl install --set profile=default -y
# Enable automatic sidecar injection in your namespace
kubectl label namespace default istio-injection=enabled
# Verify Istio components
kubectl get pods -n istio-system
kubectl get svc -n istio-system
Step 3: Install Flagger
Flagger is installed via Helm. Add the Flagger repository and install it, specifying your mesh provider:
# Add the Flagger Helm repository
helm repo add flagger https://flagger.app
helm repo update
# Install Flagger configured for Istio
helm upgrade --install flagger flagger/flagger \
--namespace flagger-system \
--create-namespace \
--set meshProvider=istio \
--set metricsServer=http://prometheus-kube-prometheus-prometheus.monitoring:9090 \
--set selectorLabels="app.kubernetes.io/name" \
--wait
# Verify Flagger is running
kubectl get pods -n flagger-system
kubectl get crd | grep flagger
The meshProvider parameter tells Flagger which traffic routing system to interact with. Available options include: istio, linkerd, nginx, contour, traefik, and gloo.
Step 4: Deploy a Sample Application with Canary Configuration
Create a namespace and deploy a sample application with both the primary deployment and a Canary resource. First, deploy the application itself:
# Create a namespace for the demo
kubectl create namespace canary-demo
kubectl label namespace canary-demo istio-injection=enabled
# Create the deployment and service
cat <
Step 5: Create the Flagger Canary Resource
The Canary resource is the heart of Flagger's progressive delivery. It defines the analysis interval, thresholds, and metrics that govern the canary process:
cat <
Let's break down the critical fields in this configuration:
- targetRef: Points to the Kubernetes Deployment Flagger should watch for image updates
- service.gateways: The Istio gateway that handles external traffic to this service
- analysis.interval: How often Flagger checks metrics during the canary
- analysis.threshold: The number of consecutive failed metric checks that trigger a rollback
- analysis.maxWeight: The maximum percentage of traffic routed to the canary
- analysis.stepWeights: The sequence of traffic percentages as the canary progresses
- metrics: Prometheus queries that evaluate canary health. These are run against the canary pod and compared to the primary
- webhooks: External hooks for pre-rollout validation, load testing, or post-rollout notifications
Step 6: Trigger a Canary Deployment
To trigger Flagger's canary process, simply update the container image of the watched deployment. Flagger detects the change automatically:
# Update the deployment image to trigger the canary
kubectl set image deployment/myapp myapp=nginx:1.26 -n canary-demo
# Watch Flagger create the canary resources
kubectl get canary -n canary-demo -w
# In another terminal, watch the deployments
kubectl get deployments -n canary-demo -w
# View detailed events from the Canary resource
kubectl describe canary myapp -n canary-demo
When you update the image, Flagger automatically:
- Creates a
myapp-primarydeployment pinned to the old image - Creates a
myapp-canarydeployment with the new image - Creates Istio VirtualService and DestinationRule resources for traffic splitting
- Begins shifting traffic incrementally according to
stepWeights - Runs metric analysis at each interval
- Either promotes the canary to primary or rolls back on failure
Step 7: Monitoring the Canary in Real Time
You can monitor the canary's progress using Flagger's built-in logging and the Canary resource status:
# Watch Flagger controller logs
kubectl logs -n flagger-system -l app.kubernetes.io/name=flagger -f
# Check the canary status with detailed conditions
kubectl get canary myapp -n canary-demo -o json | jq '.status'
# View the traffic split configuration
kubectl get virtualservices.networking.istio.io -n canary-demo -o yaml
# See the DestinationRule for traffic weights
kubectl get destinationrules.networking.istio.io -n canary-demo -o yaml
The canary status output shows conditions like:
{
"conditions": [
{
"type": "Promoted",
"status": "True",
"lastTransitionTime": "2024-01-15T10:23:45Z"
}
],
"phase": "Succeeded",
"canaryWeight": 0,
"failedChecks": 0,
"iterations": 4,
"lastAppliedSpec": "...",
"lastPromotedSpec": "...",
"lastTransitionTime": "2024-01-15T10:23:45Z"
}
Step 8: Simulating a Failed Canary and Automatic Rollback
To see Flagger's rollback in action, deploy a deliberately broken version. Create a deployment that returns HTTP 500 errors:
# Deploy a broken version that will fail metric checks
cat <
As Flagger shifts traffic to the canary and detects failing metrics (error rates exceeding your threshold), you'll see the rollback happen automatically:
# Watch the rollback in real time
kubectl get canary myapp -n canary-demo -w
# Check failedChecks and phase
kubectl get canary myapp -n canary-demo -o json | jq '.status | {phase, failedChecks, canaryWeight}'
When the threshold of consecutive failures is reached, Flagger sets the canary weight back to 0%, deletes the canary deployment, and the primary deployment continues serving traffic with the original stable image.
Advanced Configuration Patterns
Using MetricTemplates for Reusable Metrics
MetricTemplates allow you to define metrics once and reference them across multiple Canary resources, reducing duplication:
cat <
The {{ namespace }} and {{ name }} template variables are automatically populated by Flagger with the canary's namespace and the deployment name.
Configuring Webhooks for Pre-Rollout Validation
Webhooks integrate external testing systems into the canary pipeline. Pre-rollout webhooks run before any traffic is shifted, providing a gate for integration tests:
# Example webhook configuration within a Canary resource
analysis:
webhooks:
- name: integration-tests
type: pre-rollout
url: http://test-runner.default:8080/run-tests
timeout: 5m
metadata:
testSuite: full-integration
targetEnv: canary
headers:
X-API-Key: "your-secret-key"
- name: notify-slack
type: confirm-promotion
url: http://slack-bridge.default:8080/notify
timeout: 30s
- name: notify-rollback
type: rollback
url: http://slack-bridge.default:8080/rollback-alert
timeout: 30s
Webhook types include:
- pre-rollout: Runs before any traffic is shifted to the canary
- rollout: Runs at each traffic weight step during the canary
- confirm-promotion: Runs before the canary is fully promoted to primary
- post-rollout: Runs after the canary has been promoted
- rollback: Runs when a rollback is triggered
- pre-rollout-helm: Special type for Helm release testing
Configuring Alert Notifications
Flagger can send alerts to Slack, Microsoft Teams, or Discord when canary events occur:
cat <
Integrating with Linkerd Instead of Istio
Flagger works seamlessly with Linkerd. The configuration differs primarily in the mesh provider setting and metric queries:
# Install Flagger for Linkerd
helm upgrade --install flagger flagger/flagger \
--namespace flagger-system \
--create-namespace \
--set meshProvider=linkerd \
--set metricsServer=http://prometheus-kube-prometheus-prometheus.monitoring:9090 \
--wait
# Linkerd-specific metric queries in the Canary resource
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 30s
query: |
sum(
rate(
response_total{
namespace="canary-demo",
deployment="myapp",
classification!="success"
}[1m]
)
) / sum(
rate(
response_total{
namespace="canary-demo",
deployment="myapp"
}[1m]
)
) * 100
- name: request-duration
thresholdRange:
max: 500
interval: 30s
query: |
histogram_quantile(0.99,
sum(
rate(
response_latency_ms_bucket{
namespace="canary-demo",
deployment="myapp"
}[1m]
)
) by (le)
)
NGINX Ingress Controller Integration
For environments without a service mesh, Flagger can use the NGINX ingress controller for traffic splitting:
# Install Flagger with NGINX support
helm upgrade --install flagger flagger/flagger \
--namespace flagger-system \
--create-namespace \
--set meshProvider=nginx \
--set ingressClass=nginx \
--set metricsServer=http://prometheus-kube-prometheus-prometheus.monitoring:9090 \
--wait
# Canary configuration for NGINX
spec:
service:
port: 80
hosts:
- myapp.example.com
analysis:
interval: 30s
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 30s
query: |
100 - sum(
rate(
nginx_ingress_controller_requests{
namespace="canary-demo",
service="myapp",
status!~"2[0-9][0-9]"
}[1m]
)
) / sum(
rate(
nginx_ingress_controller_requests{
namespace="canary-demo",
service="myapp"
}[1m]
)
) * 100
Best Practices for Flagger Canary Deployments
1. Define Meaningful Metric Thresholds
Your metric thresholds should reflect real SLOs (Service Level Objectives). If your service targets 99.9% availability, set the success rate minimum to 99.9. If your P99 latency SLO is 200ms, set the request duration maximum to 200. Meaningless thresholds lead to false positives or, worse, promoting broken releases.
# Example of well-tuned thresholds aligned with SLOs
metrics:
- name: request-success-rate
thresholdRange:
min: 99.9 # Matches your SLO of 99.9% availability
interval: 30s
- name: request-duration
thresholdRange:
max: 200 # Matches your P99 latency SLO of 200ms
interval: 30s
2. Use StepWeights That Match Your Traffic Pattern
Aggressive step weights (like jumping directly to 50%) work for low-risk changes. For high-risk database migrations or API changes, use gradual steps:
# Conservative progression for high-risk changes
stepWeights: [1, 5, 10, 30, 70, 100]
# Moderate progression for normal releases
stepWeights: [5, 20, 50, 100]
# Quick progression for low-risk configuration changes
stepWeights: [10, 50, 100]
3. Combine Multiple Metrics for Robust Analysis
A single metric can miss problems. Combine success rate, latency, and resource utilization for a comprehensive health picture:
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 30s
- name: request-duration-p99
thresholdRange:
max: 500
interval: 30s
- name: cpu-usage
thresholdRange:
max: 80
interval: 30s
query: |
avg(
rate(
container_cpu_usage_seconds_total{
namespace="canary-demo",
pod=~"myapp-canary-.*"
}[1m]
)
) * 100
- name: memory-usage
thresholdRange:
max: 1024
interval: 30s
query: |
avg(
container_memory_working_set_bytes{
namespace="canary-demo",
pod=~"myapp-canary-.*"
}
) / 1024 / 1024
4. Implement Pre-Rollout Webhooks for Integration Testing
Run integration tests against the canary pod before any production traffic reaches it. This catches configuration errors, missing dependencies, and startup failures early:
webhooks:
- name: smoke-test
type: pre-rollout
url: http://smoke-tester.canary-demo:8080/test
timeout: 3m
metadata:
testPlan: canary-smoke
endpoint: http://myapp-canary.canary-demo:80
- name: migration-check
type: pre-rollout
url: http://db-migration-checker.canary-demo:8080/verify
timeout: 5m
5. Set Appropriate Analysis Windows
The analysis interval and threshold together determine how long a canary must prove itself. For a service with 30s interval and threshold of 5, the minimum successful analysis period is 150 seconds (5 * 30s). Choose values that balance speed with safety:
# Fast canary for low-risk services (2.5 minutes minimum)
analysis:
interval: 30s
threshold: 5
# Extended analysis for critical services (10 minutes minimum)
analysis:
interval: 60s
threshold: 10
6. Namespace and Label Organization
Keep your Canary resources, MetricTemplates, and AlertProviders logically organized by namespace. Use consistent labels to filter Flagger's monitoring:
# Label your namespaces consistently
kubectl label namespace canary-demo \
progressive-delivery=enabled \
team=backend \
environment=production
# Use selectorLabels in Flagger's Helm values
--set selectorLabels="app.kubernetes.io/name,app.kubernetes.io/instance"
7. GitOps Integration with Flux
Flagger is part of the Flux ecosystem. Store your Canary definitions in Git alongside your application manifests. Flux automatically reconciles changes, and Flagger picks up image updates from the Git repository:
# Example Flux HelmRelease for Flagger
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: flagger
namespace: flagger-system
spec:
interval: 5m
chart:
spec:
chart: flagger
version: 1.33.x
sourceRef:
kind: HelmRepository
name: flagger
namespace: flagger-system
values:
meshProvider: istio
metricsServer: http://prometheus-kube-prometheus-prometheus.monitoring:9090
8. Monitor Flagger Itself
Flagger exports Prometheus metrics about its own operations. Set up alerts for Flagger failures to know when the canary system itself has issues:
# Sample Prometheus alert rule for Flagger
groups:
- name: flagger-alerts
rules:
- alert: FlaggerRolloutFailed
expr: |
increase(flagger_canary_rollout_failed_total[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "Flagger canary rollout failed"
description: "Canary {{ $labels.name }} in namespace {{ $labels.namespace }} has failed"
- alert: FlaggerControllerDown
expr: |
up{job="flagger"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Flagger controller is not running"
Troubleshooting Common Issues
Canary Stuck in Progress
If a canary remains in "Progressing" phase indefinitely, check:
# Check the Canary resource events
kubectl describe canary myapp -n canary-demo
# Verify Prometheus connectivity
kubectl logs -n flagger-system -l app.kubernetes.io/name=flagger | grep -i prometheus
# Check if the VirtualService was created
kubectl get virtualservices.networking.istio.io -n canary-demo
# Ensure the canary deployment exists and pods are running
kubectl get pods -n canary-demo -l app=myapp
Metric Queries Returning No Data
Test your Prometheus queries directly in the Prometheus UI before adding them to a Canary resource. Ensure namespace and service name filters match exactly:
# Port-forward Prometheus to test queries
kubectl port-forward -n monitoring svc/prometheus-kube-prometheus-prometheus 9090:9090
# In another terminal, test your query
curl -s 'http://localhost:9090/api/v1/query?query=sum(rate(istio_requests_total{destination_service_namespace="canary-demo"}[1m]))' | jq
Traffic Not Shifting
Verify that your service mesh is correctly configured and the gateway exists:
# For Istio, check the gateway
kubectl get gateway -n istio-system
# Verify sidecar injection
kubectl get pods -n canary-demo -o json | jq '.items[].spec.containers[].name' | grep istio-proxy
# Check DestinationRule weights
kubectl get destinationrules.networking.istio.io -n canary-demo -o yaml | grep -A 10 "weight"
Conclusion
Flagger transforms Kubernetes deployments from a binary pass/fail operation into a graduated, metric-driven progressive delivery pipeline. By integrating with your service mesh and monitoring stack, Flagger automates the most critical phase of software delivery—validating new versions against real traffic—while providing safety nets through automatic rollback. The combination of customizable metric thresholds, flexible traffic step weights, and webhook-driven testing gates gives teams confidence to deploy frequently without sacrificing reliability. Whether you use Istio, Linkerd, NGINX, or another provider, Flagger's consistent Canary resource model means you define your deployment strategy once and apply it across your entire service fleet. Start with conservative thresholds and step weights, build robust pre-rollout testing webhooks, and integrate Flagger into your GitOps workflow for a complete progressive delivery system that keeps your users safe while accelerating your release velocity.