Deploying Node.js Applications in PM2 Cluster Mode: A Practical Guide
Deploy a Node.js application across multiple CPU cores with PM2 cluster mode. This guide covers prerequisites, startup commands, ecosystem configuration, graceful reloads, expected checks, and recovery steps.
07 Aug 2025, 12:35 UTC

A Node.js process runs on a single thread. On a server with multiple CPU cores, one process cannot use the full hardware, and if that process crashes, the entire application goes offline. PM2 cluster mode solves this by forking multiple worker processes that share the same server port, balancing incoming TCP connections across cores and restarting failed workers automatically.
Desired outcome
Run a Node.js application as a cluster of worker processes spread across available CPU cores. The cluster should restart workers automatically after a crash, support graceful reloads that minimize downtime, and keep logs separated per worker for easier debugging.
Prerequisites
- Node.js 10 or newer installed. PM2 graceful reload requires Node 10+ and PM2 5.0+.
- PM2 installed globally:
npm install -g pm2. Run this in your terminal with permissions to install global packages. - A Node.js entry point, for example
app.js, that starts an HTTP or TCP server. - Access to bind the application port (commonly 3000) on the target machine.
- Optional but recommended: an
ecosystem.config.jsfile for repeatable, environment-specific deployments.
Focused procedure
Start the cluster from your project directory
Run the following command in the directory that contains your entry file:
pm2 start app.js -i max
The -i max flag tells PM2 to spawn as many workers as detected CPU cores. Replace app.js with your actual entry file. To pin a specific worker count, replace max with a number, for example -i 4. This command changes system state by launching new processes.
Pin the configuration in an ecosystem file
Command-line flags work for quick tests, but an ecosystem file keeps deployments consistent across machines. Create ecosystem.config.js in your project root:
module.exports = {
apps: [{
name: "my-app",
script: "./app.js",
instances: "max",
exec_mode: "cluster",
env: {
NODE_ENV: "development",
PORT: 3000
},
env_production: {
NODE_ENV: "production",
PORT: 8080
}
}]
};
Start the application with the production environment:
pm2 start ecosystem.config.js --env production
All workers inherit the same environment variables. If you need per-worker configuration, define separate ecosystem files or override variables at start time.
Apply updates with a graceful reload
After deploying new code, reload the cluster instead of restarting it:
pm2 reload my-app
PM2 restarts workers one at a time. Existing connections finish before each worker exits, so the cluster stays online. This requires PM2 5.0+ and Node 10+; older versions may drop connections during reload.
Expected checks
Use these checks to confirm the cluster is healthy:
- Worker count: run
pm2 listand confirm multiple instances appear under your application name, each with a unique ID andonlinestatus. If you used-i max, the count should match your CPU core count. - Resource distribution: run
pm2 monitand verify that CPU and memory usage are spread across workers rather than concentrated in one process. - Reload behavior: during
pm2 reload my-app, watchpm2 listand confirm that worker IDs change sequentially while the application remains reachable. - Log health: run
pm2 logs my-appand check for repeated crash-and-restart cycles. A stable cluster shows normal request logs, not constant restarts. PM2 separates logs per worker, so you can trace issues to a specific process. - Load distribution: from any machine with network access to the application, send concurrent requests, for example
ab -n 1000 -c 100 http://localhost:3000/. Responses should be served by all workers; no single worker should handle every request.
Recovery options
| Command | Purpose | Changes state |
|---|---|---|
pm2 reload my-app | Graceful worker restart, minimal downtime | Yes |
pm2 restart my-app | Hard restart of all workers | Yes |
pm2 stop my-app | Halt workers without removing the definition | Yes |
pm2 delete my-app | Remove the application from the PM2 list | Yes |
pm2 kill | Stop the PM2 daemon and every managed process | Yes |
If a deploy fails, stop the cluster with pm2 stop my-app, revert your application files to the previous version, then run pm2 restart my-app or pm2 reload my-app to bring the old code back online. Run all commands from your project directory or with the exact process name.
Limitations to plan for
- TCP load balancing only: PM2 cluster mode balances TCP connections. HTTP sticky sessions require a reverse proxy such as Nginx in front of PM2, or session state moved to an external store like Redis.
- Stateless requirement: workers do not share memory. Keep sessions and cache data in Redis, a database, or another external store. In-memory objects exist separately in each worker.
- OS resource limits: if your application uses
child_process, each worker spawns its own children. On a large cluster, you may exceed file descriptor or process limits. - Native addons: modules that maintain internal state in native code may behave unpredictably because each worker is a separate process.
- No built-in inter-worker messaging: workers cannot directly call each other. Use an external message broker or IPC if workers must coordinate.
After deployment, run pm2 describe my-app to inspect the master process and worker list, then run pm2 logs my-app --lines 50 to confirm recent activity. If the worker count is lower than expected, check whether your environment restricts process creation or whether the port is already bound by another service.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.