Achieving Zero-Downtime Node.js Deploys with PM2 Cluster Mode
Stop dropping requests during deployments. Learn how to use PM2 cluster mode and rolling reloads to achieve zero-downtime updates for Node.js apps.
20 Aug 2025, 00:04 UTC

When you restart a Node.js application to deploy a new version, there is typically a window where the process is offline, resulting in dropped connections or 502 Bad Gateway errors. The useful takeaway is that by combining PM2's cluster mode with the reload command, you can perform rolling updates where new workers start before old ones shut down, maintaining 100% availability.
The Single-Threaded Bottleneck
Node.js operates on a single-threaded event loop, meaning a single instance utilizes only one CPU core regardless of how many cores the server has. If that single process crashes or restarts during a deployment, your application is completely unreachable.
PM2 solves this through cluster mode. Instead of one process, PM2 spawns multiple worker processes (typically one per CPU core) and uses a built-in round-robin load balancer to distribute incoming TCP connections across them. This provides both horizontal scaling on a single machine and a safety net for updates.
Declarative Management with Ecosystem Files
Managing multiple instances via CLI flags is error-prone. A better engineering decision is to use an ecosystem.config.js file. This declarative configuration ensures that your production environment is reproducible and version-controlled.
Create a file named ecosystem.config.js in your project root with the following configuration:
module.exports = {
apps: [{
name: 'api-service',
script: './dist/app.js',
instances: 'max', // Spawns one worker per CPU core
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
env_staging: {
NODE_ENV: 'staging',
PORT: 3001
}
}]
};
Executing a Zero-Downtime Rolling Restart
The critical distinction in PM2 is between restart and reload. While restart kills all processes and starts them again, reload restarts workers one by one.
To deploy updates without dropping traffic, run these commands on your server (requires global PM2 installation and appropriate user permissions to manage processes):
# Start the app using the production environment config pm2 start ecosystem.config.js --env production # After updating your code/build, trigger a rolling reload pm2 reload api-service
Verification: Run pm2 list to confirm that multiple instances are running. You can monitor the sequential restart process by running pm2 logs api-service; you will see workers restarting one after another rather than all at once.
Trade-offs: Statelessness and Signal Handling
Cluster mode introduces two primary engineering constraints:
- Shared State: Since each worker is a separate process, in-memory variables (like local caches or session stores) are not shared. If a user's first request hits Worker A and the second hits Worker B, any local state in Worker A is inaccessible. You must use an external store like Redis for session management.
- Graceful Shutdowns: A
reloadis only truly "zero-downtime" if your application handles termination signals. PM2 sends aSIGINTsignal to workers. Your code must listen for this signal to stop accepting new connections and finish processing active requests before exiting.
Example of a graceful shutdown handler in Node.js:
process.on('SIGINT', () => {
server.close(() => {
console.log('Process terminated safely');
process.exit(0);
});
});
Actionable Summary
To eliminate deployment downtime, move your configuration into an ecosystem.config.js file and set exec_mode to cluster. Replace pm2 restart with pm2 reload in your CI/CD pipeline, and ensure your application implements a SIGINT listener to handle in-flight requests during the transition.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.