Zero-Downtime Deployment — Complete Implementation Guide
In this tutorial, you will learn about Zero. We cover key concepts, practical examples, and best practices to help you master this topic.
Zero-downtime deployment ensures that new application versions are deployed without interrupting service to users by coordinating instance shutdown, health checks, and traffic routing.
What You'll Learn
By the end of this tutorial, you will understand rolling updates, blue-green deployment, canary releases, and how graceful shutdown enables each Strategy.
Why It Matters
Every deployment carries risk of downtime. Zero-downtime deployment eliminates scheduled downtime and enables frequent releases without user impact, supporting agile development and continuous delivery.
Real-World Use
DodaTech deploys 20+ times per day across all services. Each deployment uses a rolling update with a 40-second grace period, readiness probes that catch startup issues, and automated rollback on failure.
Zero-Downtime Deployment Learning Path
flowchart LR
A[Kubernetes Termination] --> B[Zero-Downtime Deploy]
B --> C[Rolling Update]
B --> D[Blue-Green]
B --> E[Canary]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Rolling Update Strategy
Rolling updates replace instances one at a time, keeping the service available throughout.
class RollingUpdateSimulator {
constructor(totalInstances, options = {}) {
this.instances = Array.from({ length: totalInstances }, (_, i) => ({
id: i + 1,
version: "v1",
healthy: true
}));
this.maxSurge = options.maxSurge || 1;
this.maxUnavailable = options.maxUnavailable || 0;
}
async deploy(newVersion) {
console.log(`Deploying ${newVersion} with rolling update`);
console.log(`Instances: ${this.instances.length}, MaxUnavailable: ${this.maxUnavailable}`);
for (let i = 0; i < this.instances.length; i++) {
const instance = this.instances[i];
console.log(`\nUpdating instance ${instance.id}`);
instance.healthy = false;
console.log(` Instance ${instance.id}: health check unhealthy`);
await this.sleep(500);
console.log(` Instance ${instance.id}: endpoint removed from service`);
await this.sleep(200);
console.log(` Instance ${instance.id}: SIGTERM received, draining`);
instance.version = newVersion;
instance.healthy = true;
console.log(` Instance ${instance.id}: ${newVersion} started, health check healthy`);
await this.sleep(500);
console.log(` Instance ${instance.id}: endpoint added to service`);
}
console.log(`\nAll instances updated to ${newVersion}`);
}
sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}
}
const rolling = new RollingUpdateSimulator(3);
rolling.deploy("v2");
// Deploying v2 with rolling update
// Updating instance 1
// Instance 1: health check unhealthy
// Instance 1: endpoint removed from service
// Instance 1: SIGTERM received, draining
// Instance 1: v2 started, health check healthy
// Instance 1: endpoint added to service
// Updating instance 2 ...
Blue-Green Deployment Strategy
Blue-green deployment maintains two full environments and switches traffic atomically.
class BlueGreenDeployer {
constructor() {
this.blue = { version: "v1", healthy: true, instances: 5 };
this.green = { version: null, healthy: false, instances: 5 };
this.active = "blue";
}
async deploy(newVersion, targetEnv = "green") {
const env = targetEnv === "green" ? this.green : this.blue;
console.log(`Deploying ${newVersion} to ${targetEnv} environment`);
env.version = newVersion;
console.log(" Starting instances...");
await this.sleep(1000);
console.log(" Running health checks...");
await this.sleep(500);
env.healthy = true;
console.log(`${targetEnv} environment healthy and ready`);
console.log(`Switching traffic from ${this.active} to ${targetEnv}`);
await this.sleep(200);
this.active = targetEnv;
console.log(`Active environment: ${targetEnv} (${newVersion})`);
}
rollback() {
const previous = this.active === "blue" ? "green" : "blue";
console.log(`Rolling back to ${previous}`);
this.active = previous;
}
sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}
}
const bg = new BlueGreenDeployer();
bg.deploy("v2", "green");
// Deploying v2 to green environment
// Starting instances...
// Running health checks...
// green environment healthy and ready
// Switching traffic from blue to green
// Active environment: green (v2)
Canary Release Strategy
Canary releases route a small percentage of traffic to the new version before full rollout.
class CanaryRelease {
constructor(options = {}) {
this.stableVersion = { name: "v1", instances: 10 };
this.canaryVersion = null;
this.canaryInstances = 0;
this.steps = options.steps || [5, 25, 50, 100];
this.currentStep = 0;
}
async release(newVersion) {
this.canaryVersion = { name: newVersion, instances: 0 };
for (const trafficPercent of this.steps) {
this.currentStep++;
const canaryCount = Math.ceil(this.stableVersion.instances * (trafficPercent / 100));
console.log(`Step ${this.currentStep}: ${trafficPercent}% traffic to ${newVersion}`);
console.log(` Starting ${canaryCount} canary instances`);
await this.sleep(500);
if (trafficPercent === 100) {
console.log(` Full rollout: ${newVersion} is now stable`);
this.stableVersion = { name: newVersion, instances: this.stableVersion.instances };
}
}
}
async analyzeCanaryMetrics() {
console.log(" Error rate: 0.02% (below threshold of 0.5%)");
console.log(" Latency p99: 120ms (below threshold of 200ms)");
return { passed: true };
}
sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}
}
const canary = new CanaryRelease({ steps: [10, 50, 100] });
canary.release("v2");
// Step 1: 10% traffic to v2
// Starting 1 canary instances
// Step 2: 50% traffic to v2
// Starting 5 canary instances
// Step 3: 100% traffic to v2
// Full rollout: v2 is now stable
Load Balancer Drain Integration
During deployment, the load balancer must stop sending traffic to draining instances.
class LoadBalancerDrainer {
constructor() {
this.backends = new Map();
}
register(id, healthCheckUrl) {
this.backends.set(id, {
healthCheckUrl,
activeConnections: 0,
draining: false
});
}
async drain(id) {
const backend = this.backends.get(id);
if (!backend) return;
console.log(`Load balancer draining backend ${id}`);
backend.draining = true;
// Stop health checks returning healthy
backend.healthy = false;
const start = Date.now();
while (backend.activeConnections > 0) {
if (Date.now() - start > 10000) {
console.log(` Force removing backend ${id} with ${backend.activeConnections} connections`);
break;
}
console.log(` Waiting for ${backend.activeConnections} active connections...`);
await new Promise(r => setTimeout(r, 200));
}
this.backends.delete(id);
console.log(` Backend ${id} removed from load balancer`);
}
async healthCheck(id) {
const backend = this.backends.get(id);
if (!backend || backend.draining) return false;
return true;
}
}
const lb = new LoadBalancerDrainer();
lb.register("instance-1", "http://10.0.0.1:8080/healthz");
lb.register("instance-2", "http://10.0.0.2:8080/healthz");
console.log("Load balancer with 2 backends");
Common Mistakes
Deploying all instances at once -- Rolling update is safer than replacing all instances simultaneously. maxSurge and maxUnavailable control parallelism.
Not waiting for health checks to pass after startup -- New instances should pass startup and readiness probes before receiving traffic. Configure initialDelaySeconds appropriately.
Skipping canary analysis -- Deploying to all traffic without monitoring error rates and latency can cause widespread issues. Always analyze canary metrics.
Having insufficient capacity during rolling update -- If maxSurge=1 and maxUnavailable=1, the system runs at reduced capacity. For critical services, set maxUnavailable=0.
Not testing the rollback procedure -- Deployments fail. Test rollback regularly to ensure it works. Blue-green deployments make rollback instant.
Practice Questions
What is the difference between rolling update and blue-green deployment? Rolling update replaces instances gradually. Blue-green maintains two complete environments and switches traffic atomically.
How does a canary release reduce deployment risk? It exposes the new version to a small percentage of traffic first, monitoring for errors and performance issues before full rollout.
What Kubernetes settings control rolling update behavior? maxSurge (extra instances during update) and maxUnavailable (instances that can be down). Defaults are 25% each.
Challenge: Implement a deployment strategy selector that recommends the best strategy based on service criticality and traffic pattern.
class DeploymentStrategySelector {
static recommend(service) {
const { criticality, trafficPattern, instances, rollbackTime } = service;
if (criticality === "critical" && instances >= 3) {
return {
strategy: "blue-green",
reason: "Critical service with sufficient resources for dual environments",
rollbackTime: "instant"
};
}
if (trafficPattern === "spiky" || trafficPattern === "unpredictable") {
return {
strategy: "canary",
reason: "Spiky traffic needs gradual rollout to monitor impact",
rollbackTime: "fast"
};
}
return {
strategy: "rolling-update",
reason: "Simple and effective for most services",
rollbackTime: "moderate"
};
}
}
const result = DeploymentStrategySelector.recommend({
criticality: "critical",
trafficPattern: "steady",
instances: 5,
rollbackTime: "fast"
});
console.log("Recommended:", result.strategy, "-", result.reason);
// Recommended: blue-green - Critical service with sufficient resources for dual environments
FAQ
Mini Project
Build a deployment simulator that supports rolling update, blue-green, and canary strategies with configurable instance counts, health check timing, and rollback capability.
class DeploymentOrchestrator {
constructor(strategy, instances) {
this.strategy = strategy;
this.instances = instances;
this.activeVersion = "v1";
}
async deploy(newVersion) {
console.log(`Strategy: ${this.strategy}, Deploying: ${newVersion}`);
if (this.strategy === "rolling") {
await this.rollingDeploy(newVersion);
} else if (this.strategy === "blue-green") {
await this.blueGreenDeploy(newVersion);
} else if (this.strategy === "canary") {
await this.canaryDeploy(newVersion);
}
this.activeVersion = newVersion;
console.log(`Deployment complete. Active version: ${newVersion}`);
}
async rollingDeploy(version) {
for (let i = 0; i < this.instances; i++) {
console.log(` Instance ${i + 1}: update to ${version}`);
await new Promise(r => setTimeout(r, 100));
}
}
async blueGreenDeploy(version) {
console.log(` Starting ${this.instances} new instances with ${version}`);
await new Promise(r => setTimeout(r, 200));
console.log(" Switching traffic to new environment");
}
async canaryDeploy(version) {
console.log(" Canary: 10% -> 50% -> 100%");
await new Promise(r => setTimeout(r, 300));
}
}
const orchestrator = new DeploymentOrchestrator("blue-green", 5);
orchestrator.deploy("v2");
What's Next
Now that you understand zero-downtime deployment, learn how to implement graceful shutdown in Express.js. Then build the complete graceful shutdown project.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro