Understanding process management is essential for production Node.js applications. Here's what you need to know.
Process Information#
1// Process basics
2console.log('PID:', process.pid);
3console.log('PPID:', process.ppid);
4console.log('Platform:', process.platform);
5console.log('Architecture:', process.arch);
6console.log('Node version:', process.version);
7console.log('Working directory:', process.cwd());
8
9// Environment variables
10console.log('NODE_ENV:', process.env.NODE_ENV);
11console.log('All env:', process.env);
12
13// Memory usage
14const used = process.memoryUsage();
15console.log('Memory:', {
16 heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
17 heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
18 external: `${Math.round(used.external / 1024 / 1024)}MB`,
19 rss: `${Math.round(used.rss / 1024 / 1024)}MB`,
20});
21
22// CPU usage
23const startUsage = process.cpuUsage();
24// ... do work ...
25const endUsage = process.cpuUsage(startUsage);
26console.log('CPU time:', {
27 user: `${endUsage.user / 1000}ms`,
28 system: `${endUsage.system / 1000}ms`,
29});
30
31// Uptime
32console.log('Uptime:', process.uptime(), 'seconds');Signal Handling#
1// Common signals
2// SIGTERM - Graceful shutdown request
3// SIGINT - Ctrl+C interrupt
4// SIGHUP - Terminal closed
5// SIGKILL - Force kill (cannot be caught)
6
7// Handle SIGTERM (graceful shutdown)
8process.on('SIGTERM', () => {
9 console.log('SIGTERM received, shutting down...');
10 gracefulShutdown();
11});
12
13// Handle SIGINT (Ctrl+C)
14process.on('SIGINT', () => {
15 console.log('SIGINT received, shutting down...');
16 gracefulShutdown();
17});
18
19// Handle SIGHUP (terminal closed)
20process.on('SIGHUP', () => {
21 console.log('SIGHUP received');
22 // Reload configuration, rotate logs, etc.
23});
24
25// Windows equivalent
26if (process.platform === 'win32') {
27 const readline = require('readline');
28 const rl = readline.createInterface({
29 input: process.stdin,
30 output: process.stdout,
31 });
32
33 rl.on('SIGINT', () => {
34 process.emit('SIGINT');
35 });
36}Graceful Shutdown#
1const http = require('http');
2
3class GracefulShutdown {
4 constructor(server, options = {}) {
5 this.server = server;
6 this.timeout = options.timeout || 30000;
7 this.connections = new Set();
8 this.shuttingDown = false;
9
10 // Track connections
11 server.on('connection', (conn) => {
12 this.connections.add(conn);
13 conn.on('close', () => this.connections.delete(conn));
14 });
15 }
16
17 async shutdown(signal) {
18 if (this.shuttingDown) return;
19 this.shuttingDown = true;
20
21 console.log(`${signal} received, starting graceful shutdown...`);
22
23 // Stop accepting new connections
24 this.server.close(() => {
25 console.log('Server closed');
26 });
27
28 // Set timeout for forced shutdown
29 const forceShutdown = setTimeout(() => {
30 console.error('Forcing shutdown...');
31 process.exit(1);
32 }, this.timeout);
33
34 try {
35 // Close database connections
36 await this.closeDatabase();
37
38 // Finish processing requests
39 await this.closeConnections();
40
41 // Clear timeout and exit
42 clearTimeout(forceShutdown);
43 console.log('Graceful shutdown complete');
44 process.exit(0);
45 } catch (error) {
46 console.error('Error during shutdown:', error);
47 process.exit(1);
48 }
49 }
50
51 async closeConnections() {
52 // End idle connections
53 for (const conn of this.connections) {
54 conn.end();
55 }
56
57 // Wait for connections to close
58 await new Promise((resolve) => {
59 const checkConnections = () => {
60 if (this.connections.size === 0) {
61 resolve();
62 } else {
63 setTimeout(checkConnections, 100);
64 }
65 };
66 checkConnections();
67 });
68 }
69
70 async closeDatabase() {
71 // Close database pool
72 if (global.db) {
73 await global.db.end();
74 }
75 }
76}
77
78// Usage
79const server = http.createServer(app);
80const shutdown = new GracefulShutdown(server);
81
82process.on('SIGTERM', () => shutdown.shutdown('SIGTERM'));
83process.on('SIGINT', () => shutdown.shutdown('SIGINT'));Child Processes#
1const { spawn, exec, execFile, fork } = require('child_process');
2
3// spawn - Stream output
4const ls = spawn('ls', ['-la']);
5
6ls.stdout.on('data', (data) => {
7 console.log(`stdout: ${data}`);
8});
9
10ls.stderr.on('data', (data) => {
11 console.error(`stderr: ${data}`);
12});
13
14ls.on('close', (code) => {
15 console.log(`Process exited with code ${code}`);
16});
17
18// exec - Buffer output
19exec('ls -la', (error, stdout, stderr) => {
20 if (error) {
21 console.error(`Error: ${error.message}`);
22 return;
23 }
24 console.log(`stdout: ${stdout}`);
25});
26
27// exec with promise
28const { promisify } = require('util');
29const execPromise = promisify(exec);
30
31async function runCommand() {
32 const { stdout } = await execPromise('ls -la');
33 console.log(stdout);
34}
35
36// fork - For Node.js scripts
37const child = fork('./worker.js');
38
39child.send({ task: 'process', data: [1, 2, 3] });
40
41child.on('message', (result) => {
42 console.log('Result:', result);
43});
44
45// worker.js
46process.on('message', (msg) => {
47 const result = processData(msg.data);
48 process.send(result);
49});Process Pool#
1const { fork } = require('child_process');
2const os = require('os');
3
4class WorkerPool {
5 constructor(workerScript, poolSize = os.cpus().length) {
6 this.workerScript = workerScript;
7 this.poolSize = poolSize;
8 this.workers = [];
9 this.queue = [];
10
11 this.initialize();
12 }
13
14 initialize() {
15 for (let i = 0; i < this.poolSize; i++) {
16 this.createWorker();
17 }
18 }
19
20 createWorker() {
21 const worker = fork(this.workerScript);
22
23 worker.on('message', (result) => {
24 worker.busy = false;
25 worker.currentTask?.resolve(result);
26 this.processQueue();
27 });
28
29 worker.on('error', (error) => {
30 worker.currentTask?.reject(error);
31 this.replaceWorker(worker);
32 });
33
34 worker.on('exit', (code) => {
35 if (code !== 0) {
36 this.replaceWorker(worker);
37 }
38 });
39
40 worker.busy = false;
41 this.workers.push(worker);
42 }
43
44 replaceWorker(deadWorker) {
45 const index = this.workers.indexOf(deadWorker);
46 if (index !== -1) {
47 this.workers.splice(index, 1);
48 this.createWorker();
49 }
50 }
51
52 async execute(task) {
53 return new Promise((resolve, reject) => {
54 this.queue.push({ task, resolve, reject });
55 this.processQueue();
56 });
57 }
58
59 processQueue() {
60 if (this.queue.length === 0) return;
61
62 const availableWorker = this.workers.find((w) => !w.busy);
63 if (!availableWorker) return;
64
65 const { task, resolve, reject } = this.queue.shift();
66
67 availableWorker.busy = true;
68 availableWorker.currentTask = { resolve, reject };
69 availableWorker.send(task);
70 }
71
72 async shutdown() {
73 await Promise.all(
74 this.workers.map(
75 (worker) =>
76 new Promise((resolve) => {
77 worker.once('exit', resolve);
78 worker.kill('SIGTERM');
79 })
80 )
81 );
82 }
83}
84
85// Usage
86const pool = new WorkerPool('./worker.js', 4);
87
88const results = await Promise.all([
89 pool.execute({ type: 'compute', data: 1 }),
90 pool.execute({ type: 'compute', data: 2 }),
91 pool.execute({ type: 'compute', data: 3 }),
92]);Error Handling#
1// Uncaught exceptions
2process.on('uncaughtException', (error) => {
3 console.error('Uncaught Exception:', error);
4 // Log error, clean up, then exit
5 process.exit(1);
6});
7
8// Unhandled promise rejections
9process.on('unhandledRejection', (reason, promise) => {
10 console.error('Unhandled Rejection:', reason);
11 // In Node.js 15+, this will crash the process by default
12});
13
14// Better: Use a proper error handler
15process.on('uncaughtException', async (error) => {
16 console.error('Uncaught Exception:', error);
17
18 try {
19 // Log to error monitoring service
20 await logToService(error);
21
22 // Graceful shutdown
23 await gracefulShutdown();
24 } catch (shutdownError) {
25 console.error('Shutdown error:', shutdownError);
26 }
27
28 process.exit(1);
29});
30
31// Warning events
32process.on('warning', (warning) => {
33 console.warn('Warning:', warning.name, warning.message);
34});Exit Codes#
1// Common exit codes
2// 0 - Success
3// 1 - General error
4// 2 - Misuse of command
5// 126 - Command not executable
6// 127 - Command not found
7// 128+N - Fatal signal N
8
9// Exit with code
10process.exit(0); // Success
11process.exit(1); // Error
12
13// Set exit code without exiting
14process.exitCode = 1;
15
16// Exit event
17process.on('exit', (code) => {
18 console.log(`Exiting with code ${code}`);
19 // Only synchronous operations work here
20});
21
22// Before exit (can be async)
23process.on('beforeExit', async (code) => {
24 console.log(`Before exit with code ${code}`);
25 // Can do async operations
26 await cleanup();
27});Health Checks#
1const http = require('http');
2
3class HealthChecker {
4 constructor() {
5 this.checks = new Map();
6 }
7
8 addCheck(name, checkFn) {
9 this.checks.set(name, checkFn);
10 }
11
12 async runChecks() {
13 const results = {};
14 let healthy = true;
15
16 for (const [name, checkFn] of this.checks) {
17 try {
18 await checkFn();
19 results[name] = { status: 'healthy' };
20 } catch (error) {
21 results[name] = { status: 'unhealthy', error: error.message };
22 healthy = false;
23 }
24 }
25
26 return { healthy, checks: results };
27 }
28}
29
30// Usage
31const health = new HealthChecker();
32
33health.addCheck('database', async () => {
34 await db.query('SELECT 1');
35});
36
37health.addCheck('redis', async () => {
38 await redis.ping();
39});
40
41health.addCheck('memory', async () => {
42 const used = process.memoryUsage();
43 if (used.heapUsed > 500 * 1024 * 1024) {
44 throw new Error('Memory usage too high');
45 }
46});
47
48// Health endpoint
49app.get('/health', async (req, res) => {
50 const result = await health.runChecks();
51 res.status(result.healthy ? 200 : 503).json(result);
52});Best Practices#
Signals:
✓ Handle SIGTERM for graceful shutdown
✓ Handle SIGINT for development
✓ Set timeout for forced shutdown
✓ Log shutdown progress
Error Handling:
✓ Catch uncaught exceptions
✓ Handle unhandled rejections
✓ Exit after uncaught exceptions
✓ Use proper exit codes
Production:
✓ Implement health checks
✓ Monitor memory usage
✓ Use process managers (PM2)
✓ Log to external service
Conclusion#
Proper process management ensures reliable Node.js applications. Handle signals for graceful shutdown, manage child processes carefully, and always have proper error handling. Use health checks and monitoring for production visibility.