Scaling with the cluster Module
The cluster module lets a single Node application fork itself into multiple worker processes — one per CPU core — that all share the same server port, so your server can use the whole machine instead of just one core.
Learn Scaling with the cluster Module in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn why one Node process only ever uses one core, how the primary/worker model and cluster.fork() spread load across all of them, how workers share a single port, how cluster differs from worker_threads, and why PM2's cluster mode is the practical way to do this in production.
What You'll Learn in This Lesson
1️⃣ One Process = One Core
Node runs your JavaScript on a single thread , inside a single process, on a single CPU core . That's by design — the event loop is what makes async I/O so simple. But it means a server with 4 or 16 cores still funnels everything through one of them, leaving the rest idle even under heavy load.
The first step in scaling is simply knowing how many cores you have. os.cpus() returns an array with one entry per logical core, so os.cpus().length is the number of workers you'd typically fork.
2️⃣ The Primary/Worker Model
The cluster module splits your program into two roles. The primary process doesn't serve requests — it forks workers (one per core) with cluster.fork() and keeps them alive, re-forking any that crash. Each worker is a full copy of your app that actually runs the HTTP server. The same file runs in both roles; cluster.isPrimary tells you which one you are.
The clever part is the shared port. Every worker calls server.listen(3000) , yet there's no "address in use" error: the primary owns the real socket and distributes incoming connections to the workers (round-robin by default on Linux/macOS).
3️⃣ How Round-Robin Dispatch Works
You can't easily watch the primary's scheduler from a single curl, so here's a faithful, runnable model of it. The primary keeps a pointer to the "next" worker and advances it for each connection, wrapping back to the first worker after the last one. That's the whole round-robin algorithm — and you can reproduce the exact output below.
Your turn. The round-robin picker below works once you fill in the single blank marked ___ . Follow the 👉 hint, then run it and compare with the expected output.
No blanks this time — just a brief and an outline. Write a small script that reads your core count and prints how many workers you'd fork, the worker labels, and the shared port. Run it and compare with the example output.
📋 Quick Reference — cluster vs worker_threads
Practice quiz
How many CPU cores does a single Node process use for your JavaScript?
- All of them
- Two
- One
- Half
Answer: One. One Node process runs your JavaScript on a single CPU core, leaving the others idle.
What does the cluster module let you do?
- Fork worker processes, often one per core, that share a port
- Create threads that share memory
- Run code on the GPU
- Compress HTTP responses
Answer: Fork worker processes, often one per core, that share a port. cluster forks worker processes (commonly one per core) that all share the same server port.
Which property tells you the number of logical CPU cores?
- process.cores
- cluster.workers.size
- os.cores
- os.cpus().length
Answer: os.cpus().length. os.cpus() returns an array of cores; its .length is the count you can spread work across.
In the primary/worker model, what does the primary process do?
- Handles every HTTP request itself
- Forks workers and replaces them when they die
- Shares variables with workers
- Runs the database
Answer: Forks workers and replaces them when they die. The primary forks one worker per core and, on the 'exit' event, forks a replacement to keep capacity constant.
Do clustered workers share memory or variables?
- No, each worker is a separate process with its own memory
- Yes, all variables are shared
- Only numbers are shared
- Yes, through global scope
Answer: No, each worker is a separate process with its own memory. Each worker is a separate OS process with isolated memory; shared state must live in Redis or a database.
How do all workers listen on the same port without EADDRINUSE?
- Each worker binds a different port internally
- Node disables the port check
- The primary binds the socket and hands connections to workers
- Workers take turns rebinding
Answer: The primary binds the socket and hands connections to workers. Only the primary truly binds the port; the cluster module hands workers a reference to that shared socket.
How does the primary distribute connections by default on Linux/macOS?
- Random
- Round-robin
- Least-connections
- First worker only
Answer: Round-robin. By default the primary uses a round-robin scheduler to hand each connection to the next worker.
What is the key difference between cluster and worker_threads?
- They are identical
- worker_threads scales servers; cluster does CPU work
- cluster shares memory; worker_threads does not
- cluster forks processes to scale a server; worker_threads run threads for CPU work
Answer: cluster forks processes to scale a server; worker_threads run threads for CPU work. cluster scales a network server across cores; worker_threads offloads CPU-heavy work without blocking the main thread.
If each worker keeps its own request counter, what is true?
- All workers share one counter
- Each counts only its own requests, not a global total
- The primary sums them automatically
- The counter resets every request
Answer: Each counts only its own requests, not a global total. Because memory is not shared, each worker counts only its own requests; a global total needs external storage.
What does running pm2 start app.js -i max do?
- Runs a single process only
- Disables clustering
- Launches one worker per core in cluster mode with auto-restart
- Builds the project
Answer: Launches one worker per core in cluster mode with auto-restart. PM2's cluster mode wraps cluster.fork(), launching one worker per core and restarting crashed workers.