Skip to content

Node.js Cluster vs Multiple Instances: Which Should You Choose?

Problem

When I deployed my Node.js application to a multi-core server, I noticed it only used one CPU core. I faced a choice: use the built-in cluster module, or run multiple instances with a container orchestrator?

Environment

  • Node.js 20.x
  • Multi-core Linux server (8 CPUs)
  • Considering Kubernetes deployment
  • Production traffic: ~200 RPS

What happened?

I researched both approaches and found strong opinions from developers who’ve been burned by clustering.

The consensus from production experience:

InsightScoreTakeaway
”Clustering is just a couple of lines”6Easy to implement as a quick win
”Stay away from clustering - it’s unpredictable”4Clustering causes hard-to-debug issues
”Scale out to multiple instances first”3Horizontal scaling should come first
”You need replicas for zero-downtime upgrades anyway”3Multiple instances required for production
”Clustering got us to 150 req/sec as a stop gap”-Temporary solution while fixing real issues

How do they compare?

Option #1: Node.js Cluster Module

The cluster module spawns multiple worker processes within the same application:

cluster.js
const cluster = require('cluster')
const numCPUs = require('os').cpus().length
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`)
// Fork workers (leave 1 CPU for primary)
for (let i = 0; i < numCPUs - 1; i++) {
cluster.fork()
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`)
cluster.fork() // Restart worker
})
} else {
// Worker process - run your app
require('./app')
console.log(`Worker ${process.pid} started`)
}

Pros:

  • Extremely simple to implement (a few lines of code)
  • Maximizes CPU utilization on a single server
  • No infrastructure changes required

Cons:

  • Unpredictable behavior - Workers share resources and affect each other
  • Memory bloat (each worker has its own V8 instance)
  • Complex debugging across multiple processes
  • No isolation - one crashing worker can affect others
  • Harder to implement zero-downtime deployments
  • Limited by single machine resources

Option #2: PM2 Cluster Mode

PM2 wraps the cluster module with better process management:

ecosystem.config.js
module.exports = {
apps: [{
name: 'my-app',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
kill_timeout: 5000,
wait_ready: true,
listen_timeout: 3000
}]
}
Start PM2 cluster
pm2 start ecosystem.config.js

This is slightly better than raw cluster, but still has the same fundamental issues.

Option #3: Multiple Instances with Kubernetes

This is the recommended approach for production:

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-app
spec:
replicas: 3 # Multiple independent instances
selector:
matchLabels:
app: nodejs-app
template:
metadata:
labels:
app: nodejs-app
spec:
containers:
- name: nodejs-app
image: my-nodejs-app:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nodejs-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nodejs-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70

Pros:

  • Process isolation - Each instance is independent
  • Predictable performance - No shared resource contention
  • Zero-downtime deployments - Roll out updates gradually
  • Easier debugging - Single process logs and metrics
  • Better fault tolerance - One instance failure doesn’t cascade
  • Unlimited scaling - Add more instances as needed

Cons:

  • Requires infrastructure setup (Docker, K8s, load balancer)
  • Slightly more complex initial configuration

Option #4: Docker Compose (Simple Multiple Instances)

For simpler setups without Kubernetes:

docker-compose.yml
version: '3.8'
services:
app:
image: my-nodejs-app:latest
deploy:
replicas: 3
resources:
limits:
cpus: '1'
memory: 512M
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app

Why clustering causes problems

The key reason developers recommend against clustering:

  1. Unpredictable behavior - Workers share the same OS resources, so one misbehaving worker affects all others
  2. Memory bloat - Each worker runs its own V8 instance (30-50MB base overhead each)
  3. Debugging nightmares - When something goes wrong, reproducing the issue across multiple processes is hard
  4. Zero-downtime complexity - Graceful reloads require sophisticated coordination

One developer put it bluntly: “Stay away from clustering - it’s really unpredictable.”

When to use what?

ScenarioRecommended Approach
Small app, single server, quick winCluster module (temporary)
Production app, need reliabilityMultiple instances
Need zero-downtime deploymentsMultiple instances (K8s/Docker)
Budget constrained, max existing serverCluster module
Long-term scaling strategyMultiple instances
Microservices architectureMultiple instances

Summary

In this post, I compared Node.js cluster module vs multiple instances for scaling. The key point is: use clustering only as a temporary measure to maximize resources on existing servers. For any production application, invest in proper horizontal scaling with multiple instances via Kubernetes, Docker, or ECS.

The extra effort to set up proper horizontal scaling pays off immediately in easier debugging, predictable behavior, and seamless deployments. Clustering is “unpredictable” and should be avoided as a long-term strategy.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments