Enable PM2 Cluster Mode for Zero‑Downtime Scaling of a Node.js Application
Learn how to run multiple instances of a Node.js app with PM2 in cluster mode, verify the setup, and perform seamless reloads without dropping connections.
23 Aug 2025, 21:21 UTC

Desired outcome
Run a Node.js application across all available CPU cores using PM2’s cluster mode, allowing the service to scale horizontally and be reloaded or restarted without dropping incoming requests.
Prerequisites
- Node.js version 12 or newer installed (
node --version). - PM2 installed globally (
npm i -g pm2). This requires permission to write to the global npm directory (typically root or sudo on Linux/macOS, or an administrator prompt on Windows). - An existing Node.js project that can be started with a script (e.g.,
npm startor a directnode index.js). - Access to a terminal where you can bind to the desired network port (usually
80,443, or a high port like3000).
Focused procedure
- Create an ecosystem configuration file
In the root of your project, create
ecosystem.config.jswith the following content. This tells PM2 to launch the app in cluster mode and to use as many instances as there are CPU cores.module.exports = { apps : [ { name : 'my-node-app', script : './index.js', // adjust to your entry point instances : 'max', // use all CPU cores exec_mode : 'cluster', env : { NODE_ENV: 'development' }, env_production : { NODE_ENV: 'production' } } ] }; - Start the application via PM2
Run the start command from the project directory. You may need to prefix with
sudoif the app binds to a privileged port (<1024).pm2 start ecosystem.config.js - Verify that cluster mode is active
After the command finishes, list the managed processes:
pm2 listYou should see multiple entries under the same app name, each with a unique
idand themodecolumn showingcluster. The total number of instances should match the output ofnproc(oros.cpus().lengthin Node). - Confirm load distribution
Add a simple log statement to your application that prints the cluster worker ID on each request. For example, in an Express handler:
app.get('/', (req, res) => { console.log(`Worker ${process.env.NODE_APP_INSTANCE} handling request`); res.send('OK'); });Restart the app (
pm2 restart ecosystem.config.js) to pick up the change, then generate traffic (e.g., withcurl http://localhost:3000/in a loop) and observe the console output. Different worker IDs should appear, indicating that requests are being spread across instances. - Perform a zero‑downtime reload
To update the code or change environment variables without dropping connections, use the reload command:
pm2 reload ecosystem.config.jsPM2 will start new instances, gracefully shut down the old ones, and keep the service available throughout the process.
Expected checks
pm2 listshows themodecolumn asclusterfor each instance and theinstancescount matches the expected number (usually CPU core count).pm2 monitdisplays CPU usage spread relatively evenly across the worker processes.- Request logs (as shown in the verification step) rotate through different worker IDs, confirming load balancing.
- After issuing
pm2 reload ecosystem.config.js, monitoring tools (e.g., response time metrics from your load balancer or a simplecurl -w "%{time_total}"loop) should not show a spike in error rates or latency.
Recovery options
- Automatic restarts: If any worker crashes, PM2 will automatically respawn it based on the
restartpolicy (default is unlimited). - Snapshot and restore: To save the current process list for later recovery, run
pm2 save. This creates a dump file (~/.pm2/dump.pm2) that can be restored withpm2 resurrect. - Revert to a known good configuration: Stop the cluster with
pm2 stop all, then start again from a previously verifiedecosystem.config.jsusingpm2 start ecosystem.config.js. - Manual rollback of code: If a bad deploy caused issues, you can revert your source code to a prior commit and run
pm2 reload ecosystem.config.jsto roll out the corrected version.
Limitations and considerations
- The application must be stateless or rely on an external store (e.g., Redis, a database) for any session data. In‑memory variables are not shared between cluster workers.
- Setting
instancesto a value higher than the number of CPU cores can cause excessive context switching and degrade performance. Always test under realistic load to find the optimal count. - Port binding requires sufficient privileges; if you encounter
EACCESerrors, either run with elevated permissions or configure your system to allow non‑root binding to the desired port (e.g., usingauthbindon Linux). - Windows support for cluster mode exists but may have subtle differences in signal handling; verify behavior on your target deployment platform.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.