Chapter 39: Node.js Process Management
Process Management in Node.js — written as if we are sitting together, with the terminal open, code editor ready, and I’m explaining everything slowly and clearly, with many realistic examples you can copy-paste and try right now.
We’ll cover:
- What a “process” really is in Node.js
- The most important properties and methods of the global process object
- How to manage the current process (arguments, signals, exit, env, memory…)
- How to create and manage child processes (the child_process module)
- Real-world patterns used in production in 2025–2026
- Common mistakes & best practices
Let’s go step by step.
1. The global process object – your window into the current Node.js process
process is a global object — you don’t need to import anything.
It gives you information about and control over the currently running Node.js process.
Most useful properties & methods (with real examples)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
// process.argv – command-line arguments console.log(process.argv); // Example run: node app.js --port 4000 --debug // → [ '/usr/bin/node', '/path/to/app.js', '--port', '4000', '--debug' ] // process.argv.slice(2) is what people usually use const args = process.argv.slice(2); console.log(args); // [ '--port', '4000', '--debug' ] // process.env – environment variables console.log(process.env.NODE_ENV); // "development" or "production" console.log(process.env.PORT); // "3000" or undefined console.log(process.env.DATABASE_URL); // your connection string // process.cwd() – current working directory console.log(process.cwd()); // /home/aman/projects/my-api // process.chdir() – change working directory process.chdir('/tmp'); console.log(process.cwd()); // now /tmp // process.pid – process ID console.log(`My PID is ${process.pid}`); // e.g. 12345 // process.platform – operating system console.log(process.platform); // 'linux', 'win32', 'darwin' // process.arch – CPU architecture console.log(process.arch); // 'x64', 'arm64', 'ia32' // process.memoryUsage() – memory statistics console.log(process.memoryUsage()); // { // rss: 52428800, // resident set size // heapTotal: 10485760, // heapUsed: 4194304, // external: 123456, // arrayBuffers: 123456 // } // process.title – change process name (useful in process list) process.title = 'my-cool-api-server'; |
Very common pattern – read CLI flags nicely
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function getCliFlags() { const args = process.argv.slice(2); const flags = {}; for (let i = 0; i < args.length; i++) { if (args[i].startsWith('--')) { const key = args[i].slice(2); const value = args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : true; flags[key] = value; if (value !== true) i++; } } return flags; } const flags = getCliFlags(); console.log(flags); // { port: '4000', debug: true, watch: true } |
2. Graceful shutdown & signal handling (very important in production)
Node.js processes can be told to stop in several ways:
- Ctrl+C (SIGINT)
- kill <pid> (default SIGTERM)
- kill -9 <pid> (SIGKILL – cannot be caught)
- Docker stop, PM2 restart, systemd, etc.
Best practice – graceful shutdown
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// graceful-shutdown.js import http from 'node:http'; let server: http.Server | null = null; let shuttingDown = false; function startServer() { server = http.createServer((req, res) => { res.end('Hello'); }); server.listen(3000, () => { console.log('Server listening on port 3000'); }); } function gracefulShutdown(signal: string) { if (shuttingDown) return; shuttingDown = true; console.log(`Received ${signal} – shutting down gracefully...`); // Stop accepting new connections if (server) { server.close(() => { console.log('HTTP server closed'); }); } // Give 10 seconds for ongoing requests to finish setTimeout(() => { console.log('Grace period ended – forcing exit'); process.exit(1); }, 10000); // Close database connections, queues, etc. here // prisma.$disconnect(); // redis.quit(); } process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT')); // Ctrl+C // Optional: catch uncaught exceptions (last resort) process.on('uncaughtException', (err) => { console.error('Uncaught exception:', err); gracefulShutdown('uncaughtException'); }); startServer(); |
What good production setups do
- Listen for SIGTERM (Docker stop, Kubernetes, PM2, systemd)
- Listen for SIGINT (Ctrl+C in dev)
- Close HTTP server → stop accepting new requests
- Wait a grace period (5–30 seconds)
- Close DB connections, Redis, message queues, etc.
- Exit with code 0 (success) or 1 (error)
3. Creating & managing child processes (child_process module)
Node.js is single-threaded for JavaScript execution. When you need to run CPU-heavy work or external commands, you spawn child processes.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import { spawn, exec, fork } from 'node:child_process'; // 1. spawn – streams output, good for long-running processes const child = spawn('ls', ['-la']); child.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); child.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); child.on('close', (code) => { console.log(`Process exited with code {code}`); }); // 2. exec – buffers output, good for short commands exec('git rev-parse --short HEAD', (error, stdout, stderr) => { if (error) { console.error(`error: ${error.message}`); return; } console.log(`Current commit: ${stdout.trim()}`); }); // 3. fork – special spawn for Node.js scripts (IPC channel) const worker = fork('./worker.js'); worker.on('message', (msg) => { console.log('Message from worker:', msg); }); worker.send({ task: 'compute-heavy-job' }); |
worker.js (example)
|
0 1 2 3 4 5 6 7 8 9 10 |
process.on('message', (msg) => { // Do heavy computation here... const result = heavyMath(msg.task); process.send({ result }); }); |
When to use which
| Method | Use when… | Output handling | IPC (parent ↔ child messages) | Typical use case |
|---|---|---|---|---|
| spawn | Long-running process, streaming output | Streams (stdout/stderr) | No by default | ffmpeg, image processing, git commands |
| exec | Short command, you want full output at once | Buffered (callback) | No | Quick shell commands, version checks |
| fork | Run another Node.js script, need messaging | Streams + IPC | Yes | Worker threads alternative, parallel tasks |
4. Memory & CPU usage monitoring (very useful in production)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
setInterval(() => { const used = process.memoryUsage(); console.log('Memory usage:'); console.log(` RSS: ${(used.rss / 1024 / 1024).toFixed(2)} MB`); console.log(` Heap total: ${(used.heapTotal / 1024 / 1024).toFixed(2)} MB`); console.log(` Heap used: ${(used.heapUsed / 1024 / 1024).toFixed(2)} MB`); console.log(` External: ${(used.external / 1024 / 1024).toFixed(2)} MB`); console.log(`CPU cores: {require('node:os').cpus().length}`); }, 10000); |
Very common in production Send this info to monitoring (Prometheus, Grafana, New Relic, Datadog…)
Summary – Quick cheat sheet for process management
| You want to… | Use this | Typical real-world example |
|---|---|---|
| Read CLI arguments | process.argv.slice(2) | –port 4000 –debug |
| Read environment variables | process.env.PORT | Config, secrets, feature flags |
| Graceful shutdown | process.on(‘SIGTERM’, …) + server.close() | Docker stop, Kubernetes rolling update |
| Run external command (short) | exec(‘git rev-parse HEAD’, callback) | Get commit hash, run migrations |
| Run external process (long-running) | spawn(‘ffmpeg’, args) | Video processing, image optimization |
| Run another Node.js script with IPC | fork(‘./worker.js’) | Background tasks, parallel processing |
| Monitor memory usage | process.memoryUsage() | Health checks, alerts |
| Change process name in ps / top | process.title = ‘my-api-server’ | Easier debugging in production |
Want to go much deeper into any specific area?
- Full graceful shutdown example with DB, Redis, HTTP server
- Building a real worker pool using fork + IPC
- process.memoryUsage() monitoring + alerting patterns
- Child process error handling & restart strategies
- Using PM2, Docker, Kubernetes signals with Node.js
Just tell me which topic feels most useful right now — I’ll continue with concrete, production-ready examples. 😊
