How to Implement Blue-Green and Canary Deployments in Kubernetes
Two complete, working deployment strategies for shipping a new version with minimal risk — instant-rollback blue-green, and gradual, traffic-controlled canary.
Both blue-green and canary deployments solve the same underlying problem — reducing the blast radius of a bad release — through different mechanisms. This walks through a complete, working setup for each using plain Kubernetes primitives, no additional tooling required.
Blue-Green: two full environments, instant switch
Step 1: deploy the current version as “blue”
# deployment-blue.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-blue
spec:
replicas: 3
selector:
matchLabels: {app: myapp, version: blue}
template:
metadata:
labels: {app: myapp, version: blue}
spec:
containers:
- name: myapp
image: myapp:v1.0
Step 2: point the live Service at blue
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: blue # <- this line controls which version receives traffic
ports:
- port: 80
targetPort: 8080
Step 3: deploy the new version as “green,” fully, alongside blue
# deployment-green.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-green
spec:
replicas: 3
selector:
matchLabels: {app: myapp, version: green}
template:
metadata:
labels: {app: myapp, version: green}
spec:
containers:
- name: myapp
image: myapp:v2.0
kubectl apply -f deployment-green.yaml
Green runs fully, receiving zero production traffic, while you test it directly (port-forwarding to it, or a separate internal test Service pointed at version: green).
Step 4: switch traffic instantly
kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}'
All traffic now goes to green, instantly — no rolling transition period.
Step 5: roll back just as instantly, if needed
kubectl patch service myapp -p '{"spec":{"selector":{"version":"blue"}}}'
Since blue never stopped running, rollback is exactly as instant as the cutover was — the old version was never scaled down.
Step 6: clean up the old version once confident
kubectl delete deployment myapp-blue
Canary: gradual, traffic-percentage-based rollout
Step 1: run stable and canary as separate deployments, unequal replica counts
# deployment-stable.yaml — 9 replicas
metadata: {name: myapp-stable}
spec:
replicas: 9
template:
metadata: {labels: {app: myapp, track: stable}}
spec: {containers: [{name: myapp, image: "myapp:v1.0"}]}
# deployment-canary.yaml — 1 replica
metadata: {name: myapp-canary}
spec:
replicas: 1
template:
metadata: {labels: {app: myapp, track: canary}}
spec: {containers: [{name: myapp, image: "myapp:v2.0"}]}
Step 2: one Service selecting both, by the shared label only
apiVersion: v1
kind: Service
metadata: {name: myapp}
spec:
selector: {app: myapp} # matches BOTH stable and canary pods
ports: [{port: 80, targetPort: 8080}]
Because the Service selects on app: myapp alone (not track), and kube-proxy load-balances roughly evenly across all matching pod IPs, a 9:1 replica ratio approximates 10% of traffic reaching the canary — a simple, dependency-free way to achieve percentage-based traffic splitting using nothing but replica counts.
Step 3: monitor the canary closely
kubectl logs -l track=canary --tail=100 -f
Watch error rates and latency for the canary pod specifically, comparing against the stable fleet, before deciding whether to proceed.
Step 4: gradually shift the ratio
kubectl scale deployment myapp-canary --replicas=3
kubectl scale deployment myapp-stable --replicas=7
Incrementally rebalance the replica counts toward the canary as confidence grows, watching metrics at each step.
Step 5: complete or abort the rollout
To complete: scale canary to the full replica count and remove myapp-stable entirely. To abort: scale canary back to 0 and delete it, leaving stable fully serving traffic exactly as before the canary was introduced.
Choosing between them
Blue-green is the right choice when you need an instant, all-or-nothing cutover and rollback — appropriate for changes you’re fairly confident about but want a fast escape hatch for. Canary is the right choice when you want to limit exposure to a smaller fraction of real traffic first, catching a problem before it affects everyone, at the cost of a more gradual, closely-monitored rollout process. Production systems handling significant traffic commonly use a proper service mesh (Istio, Linkerd) for precise percentage-based canary routing rather than the replica-ratio approximation shown here — this walkthrough uses only plain Kubernetes primitives specifically to show the underlying mechanism without additional infrastructure dependencies.