Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Production-Grade Node.js Process Management with PM2

Tech May 8 3

Introduction to PM2

PM2 serves as a production-level process manager tailored for Node.js applications. It provides essential features such as load balancing, automatic process resurrection, centralized log administration, and real-time system metrics. By operating applications as daemon processes, PM2 guarantees service continuity even during unexpected crashes or system reboots. The tool also facilitates zero-downtime deployments and integrates seamlessly with containerized environments like Docker.

Core Capabilities

PM2 addresses several critical operational challenges in production environments:

  • Process Resilience: Automatically revives crashed or terminated processes to maintain service availability.
  • Horizontal Scaling: Utilizes cluster mode to spawn multiple instances, distributing traffic across CPU cores.
  • Seamless Updates: Supports reloading applications without interrupting active connections.
  • Log Aggregation: Consolidates logs from multiple instances for easier debugging.
  • Resource Monitoring: Tracks memory and CPU consumption in real-time.

Installation

Install the package globally to make the CLI available system-wide:

npm install pm2 -g

Verify the installation:

pm2 --version

Essential CLI Commands

CommandDescriptionExamples
startInitialize an applicationpm2 start server.js --name api-server
stopTerminate a running processpm2 stop api-server
pm2 stop all
restartReboot an applicationpm2 restart api-server
deleteRemove a process from PM2pm2 delete api-server
listDisplay all managed processespm2 list
monitOpen the monitoring dashboardpm2 monit
logsStream application logspm2 logs --lines 200
savePersist the current process listpm2 save
reloadPerform zero-downtime reloadpm2 reload all
scaleAdjust instance countpm2 scale api-server +2
startupConfigure boot auto-startpm2 startup

Declarative Configuration with Ecosystem Files

The ecosystem.config.js file allows for repeatable and version-controlled deployments. It supports defining multiple applications and environment-specific variables within a single source.

Basic Configuration Structure

module.exports = {
  apps: [
    {
      name: 'web-service',
      script: './dist/index.js',
      instances: 'max',
      exec_mode: 'cluster',
      autorestart: true,
      watch: false,
      max_memory_restart: '2G',
      env: {
        NODE_ENV: 'development',
        PORT: 3000
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 8080
      }
    }
  ]
};

Key Configuration Fields

  • apps: Array of application configuration objects.
  • name: Unique identifier for the process.
  • script: Entry point file path.
  • instances: Number of clusters; use 'max' to match CPU cores.
  • exec_mode: Set to 'cluster' for load balancing.
  • max_memory_restart: Threshold to trigger automatic restart.
  • env: Default environment variables.
  • env_production: Variables specific to the production environment.

Launch applications using the configuration file:

pm2 start ecosystem.config.js --env production

Command-Line Configuration Alternatives

For ad-hoc operations, CLI flags can replicate configuration file functionality:

# Start with a specific name and instances
pm2 start server.js --name web-api -i 4

# Enable file watching for development
pm2 start server.js --watch

# Set memory limit for auto-restart
pm2 start server.js --max-memory-restart 1G

# Pass environment variables
pm2 start server.js --node-args="--max-old-space-size=2048"

Real-Time Monitoring

PM2 includes a terminal-based monitoring interface:

pm2 monit

This command launches an interactive dashboard displaying real-time CPU usage, memory consumption, and logs for all processes. For distributed monitoring and historical data analysis, PM2 Plus offers a web-based dashboard with alerting capabilities.

Session Management in Cluster Mode

When operating multiple instances, in-memory session storage becomes problematic because requests from the same client may be routed to different instances. External session stores resolve this issue.

Using Redis with Express for session sharing:

const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis').default;
 
const app = express();
 
app.use(
  session({
    store: new RedisStore({ url: 'redis://localhost:6379' }),
    secret: 'secure-session-key',
    resave: false,
    saveUninitialized: false,
    cookie: { secure: false }
  })
);

Graceful Shutdown Implementation

To ensure application state is preserved during restarts, handle termination signals properly:

const express = require('express');
const app = express();
 
let isShuttingDown = false;
const activeRequests = new Set();
 
app.use((req, res, next) => {
  if (isShuttingDown) {
    return res.status(503).send('Server is shutting down');
  }
  activeRequests.add(res);
  res.on('finish', () => activeRequests.delete(res));
  next();
});
 
const server = app.listen(3000);
 
const shutdownHandler = () => {
  isShuttingDown = true;
  console.log('Initiating graceful shutdown...');
 
  server.close(() => {
    console.log('HTTP server closed');
  });
 
  const checkInterval = setInterval(() => {
    if (activeRequests.size === 0) {
      clearInterval(checkInterval);
      console.log('All requests completed. Exiting.');
      process.exit(0);
    }
    console.log(`Waiting for ${activeRequests.size} requests to finish...`);
  }, 1000);
};
 
process.on('SIGTERM', shutdownHandler);
process.on('SIGINT', shutdownHandler);

Shutdown Timeout Configuration

PM2 waits a default period before forcing a process kill. Adjust this timeout if the cleanup logic requires more time:

pm2 start server.js --kill-timeout 5000

This configuration grants the application 5000 milliseconds to complete cleanup before PM2 issues a SIGKILL signal. The timeout ensures that PM2 retains control over misbehaving processes while allowing properly designed applications to shut down cleanly.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.