Uptime is critical. Whether you're running a SaaS, API, or web platform, users expect availability—always. In this guide, we'll walk through how to achieve zero downtime deployments using PM2, Docker and a reverse proxy like NGINX.
Why Zero Downtime Matters
User Experience: No interruptions during new releases
Business Continuity: Prevent revenue loss during deploys
Stack Overview
App Runtime: Node.js with PM2
Containerization: Docker
Web Server / Proxy: NGINX or Caddy
CI/CD Tool: (Drone.io (my new favorite), Jenkins, GitHub Actions, etc.)
Step 1: Use PM2 to Manage the App Process
PM2 is a Node.js process manager that supports graceful restarts, monitoring, and clustering.
Example Dockerfile with PM2
FROM node:18
WORKDIR /app
# Add build-time env vars
ARG NEXT_PUBLIC_CDN_URL
ARG NEXT_PUBLIC_CDN_HOST
ENV NEXT_PUBLIC_CDN_URL=$NEXT_PUBLIC_CDN_URL
ENV NEXT_PUBLIC_CDN_HOST=$NEXT_PUBLIC_CDN_HOST
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm install -g pm2
EXPOSE 3000
CMD ["pm2-runtime", "start", "node_modules/next/dist/bin/next", "--", "start", "-p", "3000"]
Step 2: Create a Safe Docker Build + Deploy Flow
We will:
Build the Docker image
Run it on a temporary port
Perform health checks
If OK, swap it with the current live container
Example Shell Script (simplified)
docker build --no-cache -t app-latest .
docker run -d --name app-temp -p 3132:3000 app-latest
# Wait and test health
sleep 5
if docker exec app-temp curl -s http://localhost:3000 | grep -q '<title>'; then
docker stop app-live && docker rm app-live
docker stop app-temp && docker rm app-temp
docker run -d --name app-live -p 3131:3000 app-latest
else
echo "Health check failed. Aborting deploy."
docker stop app-temp && docker rm app-temp
exit 1
fi
Port 3132 is used temporarily; 3131 is the live port behind the reverse proxy.
Step 3: Set Up Reverse Proxy with NGINX
server {
listen 80;
location / {
proxy_pass http://localhost:3131;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
NGINX routes traffic to the live container. When we swap containers on port 3131, traffic never notices.
Uptime Monitoring
Use tools like:
Uptime Kuma (Docker-based)
Better Uptime,
Upptime (hosted)
Set them to monitor /healthz or homepage on localhost:3131.
Security Tips
Avoid running Docker containers as root user
Don’t mount the Docker socket (/var/run/docker.sock) unless absolutely necessary
Use USER node or UID-based permissions in Dockerfile
Conclusion
Zero downtime deployment is achievable and maintainable with PM2, Docker, and a reverse proxy. This setup avoids user-facing errors, improves reliability, and keeps your engineering team confident in every release.
Want to go further? Add CI/CD integration with Drone or GitHub Actions and automate everything end-to-end.
That's all folks! ✨
Top comments (0)