Configuring Rolling Update Deployment for Web Applications
Rolling Update is a gradual replacement of application instances: first 1–2 pods/containers are updated, their health is checked, then the next ones. Unlike Blue-Green, it doesn't require double resources, but old and new versions briefly coexist.
Rolling Update in Kubernetes
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 6
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2 # allow up to 2 extra pods during update
maxUnavailable: 1 # max 1 pod unavailable at any time
minReadySeconds: 30 # pod considered ready 30 sec after start
selector:
matchLabels: { app: myapp }
template:
metadata:
labels: { app: myapp }
spec:
containers:
- name: myapp
image: registry.example.com/myapp:v1.1.0
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
terminationGracePeriodSeconds: 60 # allow time to finish request processing
# Update image
kubectl set image deployment/myapp myapp=registry.example.com/myapp:v1.2.0
# Watch progress
kubectl rollout status deployment/myapp
# History
kubectl rollout history deployment/myapp
# Rollback
kubectl rollout undo deployment/myapp
kubectl rollout undo deployment/myapp --to-revision=3
Graceful Shutdown
// Node.js/Express — proper shutdown on SIGTERM
process.on('SIGTERM', async () => {
console.log('SIGTERM received, shutting down gracefully');
// Stop accepting new connections
server.close(() => {
console.log('HTTP server closed');
});
// Allow time to finish current requests
await new Promise(resolve => setTimeout(resolve, 30_000));
// Close DB connections
await db.destroy();
process.exit(0);
});
// Laravel/Octane — graceful stop
// Octane handles SIGTERM itself, waiting for request completion
// Use in Dockerfile:
STOPSIGNAL SIGTERM
Rolling Update in Docker Swarm
# docker-compose.yml
services:
web:
image: registry.example.com/myapp:latest
deploy:
replicas: 4
update_config:
parallelism: 1 # update 1 at a time
delay: 30s # 30 seconds between updates
failure_action: rollback
monitor: 60s # monitor 60 sec after pod update
max_failure_ratio: 0.1
rollback_config:
parallelism: 2
delay: 0s
failure_action: pause
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
# Update service
docker service update --image registry.example.com/myapp:v1.2.0 myapp_web
# Watch
docker service ps myapp_web
# Rollback
docker service rollback myapp_web
Health Check Endpoint
Proper rolling update requires health checks distinguishing liveness and readiness:
// Laravel — health check routes
Route::get('/health/live', function () {
// Just check that the process is alive
return response()->json(['status' => 'ok']);
});
Route::get('/health/ready', function () {
// Check readiness to accept traffic
try {
DB::connection()->getPdo();
Cache::store()->get('health-check');
} catch (\Exception $e) {
return response()->json(['status' => 'not ready', 'error' => $e->getMessage()], 503);
}
return response()->json(['status' => 'ready']);
});
Problem of Incompatible Migrations
During Rolling Update, two app versions briefly run together. DB schema must be compatible with both:
Rule: Run migrations before deploying new code. Migration must be backward-compatible.
// CANNOT: rename column immediately
// Schema breaks old version
// CAN: three-stage deploy
// Deploy 1: add new_column (nullable)
// Deploy 2: fill new_column, switch code to it
// Deploy 3: drop old_column
Implementation Timeline
- Kubernetes Rolling Update with health checks: 2–3 days
- Docker Swarm rolling update: 1–2 days
- Strategy for backward-compatible migrations: 1 day







