Restarting a single container to deploy a new version causes a brief service interruption. This guide covers running multiple replicas so Docker can update them one at a time, keeping the application available throughout the deployment.
The Core Approach: Rolling Updates
Instead of one container, run multiple instances behind Nginx, updating them one at a time — at every moment, at least one instance remains available to serve traffic.
Prerequisites
- Docker Compose installed
- Nginx as a reverse proxy in front of the application
Step 1 — Run Multiple Replicas
services:
app:
image: myapp:latest
deploy:
replicas: 3
ports:
- "3000-3002:3000"
Note: the deploy.replicas key works fully with Docker Swarm; for plain Docker Compose, define multiple explicit services instead (see below) if not using Swarm mode.
Alternative for Plain Docker Compose (Non-Swarm)
services:
app1:
image: myapp:latest
ports:
- "3001:3000"
app2:
image: myapp:latest
ports:
- "3002:3000"
app3:
image: myapp:latest
ports:
- "3003:3000"
Step 2 — Configure Nginx to Load Balance Across Replicas
upstream backend {
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}
server {
location / {
proxy_pass http://backend;
}
}
Step 3 — Add Health Checks
services:
app1:
image: myapp:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
retries: 3
See How to Add Health Check Endpoints to Your Application for the application side of this.
Step 4 — Perform a Rolling Update Script
sudo nano /usr/local/bin/rolling-deploy.sh
#!/bin/bash
set -e
docker compose pull app1
docker compose up -d --no-deps app1
sleep 15
curl -f http://127.0.0.1:3001/health || exit 1
docker compose pull app2
docker compose up -d --no-deps app2
sleep 15
curl -f http://127.0.0.1:3002/health || exit 1
docker compose pull app3
docker compose up -d --no-deps app3
sleep 15
curl -f http://127.0.0.1:3003/health || exit 1
echo "Rolling deployment complete"
Each instance is updated and verified healthy before moving to the next — Nginx continues routing to the remaining healthy instances throughout, so users experience no interruption.
Step 5 — Remove Nginx's Reference to an Unhealthy Instance Temporarily (Advanced)
For extra safety, mark an instance as down in the Nginx upstream during its own update window:
upstream backend {
server 127.0.0.1:3001 down;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}
sudo systemctl reload nginx
Using Docker Swarm for Native Rolling Updates (Alternative)
Docker Swarm mode natively supports deploy.replicas and rolling update configuration without the manual scripting above:
services:
app:
image: myapp:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
order: start-first
order: start-first starts the new container before stopping the old one, ensuring capacity is never reduced during the update.
Common Errors
Brief errors during deployment despite multiple replicas — verify health checks are actually gating traffic correctly, and that Nginx isn't routing to an instance still starting up.
Database connection pool exhaustion during rolling updates — temporarily running extra instances (old + new simultaneously) increases total database connections; ensure your connection pool limits account for this overlap.
Best Practices
- Always verify health before routing traffic to a newly updated instance
- Run at least 2-3 replicas minimum for meaningful zero-downtime capability
- Consider Docker Swarm or Kubernetes if you need this pattern at larger scale
Continue Reading
- How to Add Health Check Endpoints to Your Application
- How to Set Up Basic Load Balancing with Nginx
- Docker Restart Policies and Healthchecks Explained
Browse more articles in DevOps & CI/CD.
