Skip to content
Learni
View all tutorials
Node.js

How to Deploy Node.js Apps with PM2 in 2026

Lire en français

Introduction

PM2 is an open-source process manager for Node.js, the go-to standard in 2026 for production deployments. Unlike a basic node app.js, PM2 provides automatic restarts on crashes, cluster mode for horizontal scaling, real-time monitoring via web dashboard, and zero-downtime deploys.

Why use it? Picture your API crashing at 3 AM: PM2 restarts it in milliseconds without human intervention. For beginner devs, it's like upgrading from a bicycle to an automatic car—everything's handled. This tutorial takes you from installation to production, packed with 1500+ words of pure value: full code snippets, copy-paste configs, and pitfalls avoided. By the end, your app will be robust, monitored, and scalable. Ideal for freelancers or startups wanting reliability without complex DevOps.

Prerequisites

  • Node.js 20+ installed (check with node -v)
  • npm or yarn
  • A basic Node.js app (we'll create one)
  • Unix-like terminal (WSL on Windows is fine)
  • Minimal JavaScript knowledge

Install PM2 Globally

terminal
npm install pm2@latest -g

npm audit fix --force

This command installs PM2 globally so you can use it anywhere. The @latest flag ensures the stable 2026 version (5.x+). npm audit fix patches potential vulnerabilities right away, dodging common production security pitfalls.

Create a Test Node.js App

Before diving into PM2, let's build a simple Express app for testing. It's our playground: an HTTP server responding on / and /health. Copy this code into a project folder (mkdir my-app && cd my-app). Think of it as scaffolding to validate PM2 without added complexity.

Create the Express Server

server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ message: 'Hello PM2!', timestamp: new Date().toISOString() });
});

app.get('/health', (req, res) => {
  res.status(200).json({ status: 'OK', uptime: process.uptime() });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

This complete Express server handles two routes and reads PORT from env vars for production. It's basic crash-proof. Pitfall: without PM2, Ctrl+C kills everything; here, we're prepping for daemon mode.

Install Dependencies

terminal
npm init -y
npm install express
npm install --save-dev nodemon

Initializes package.json, adds Express for the server, and nodemon for local dev (optional). In production, PM2 replaces nodemon. Avoids polluting global node_modules.

Launch the App with Basic PM2

Let's test PM2 without any config. This is the 'wow' moment: your app runs in the background and survives machine restarts. Use pm2 list to check status—like a mini dashboard.

Start the App Simply

terminal
pm2 start server.js --name mon-api
pm2 save
pm2 startup

Launches server.js as 'mon-api', saves the process list (save), and sets up boot startup (startup—follow the instructions). Pitfall: without save, a reboot loses everything.

Configure with Ecosystem File

Pro level: Switch to ecosystem.config.js for scaling. It's a declarative JS file handling clusters, instances, and env vars. Like a recipe book for PM2—reproducible on any server.

Ecosystem Config File

ecosystem.config.js
module.exports = {
  apps: [{
    name: 'mon-api-cluster',
    script: './server.js',
    instances: 'max',  // Utilise tous les CPU cores
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'development',
      PORT: 3000
    },
    env_production: {
      NODE_ENV: 'production',
      PORT: 3001
    },
    log_date_format: 'YYYY-MM-DD HH:mm Z',
    error_file: './logs/err.log',
    out_file: './logs/out.log',
    time: true
  }]
};

Defines a clustered app (max = auto-scale to all CPU cores), with dev/prod envs. Separate logs with timestamps. Restart with pm2 start ecosystem.config.js --env production. Pitfall: instances: 1 without cluster = no scaling.

Restart in Production Mode

terminal
pm2 delete all
pm2 start ecosystem.config.js --env production
pm2 save
pm2 list
pm2 monit

Deletes everything, relaunches in production (PORT=3001), saves, lists, and opens monitoring. monit is a real-time TUI dashboard (CPU, RAM, restarts). Zero downtime via clustering.

Monitoring and Logs

pm2 monit shows live metrics; pm2 logs for traces. In 2026, upgrade to PM2 Plus (paid) for a web dashboard. Test it: curl localhost:3001/health.

Useful Monitoring Commands

terminal
pm2 logs mon-api-cluster
pm2 show mon-api-cluster
pm2 restart mon-api-cluster
pm2 stop mon-api-cluster
pm2 delete mon-api-cluster

Filtered logs, app details, soft restarts (zero-downtime), stop/delete. Ideal for production debugging without interruptions. Always name apps for precise targeting.

Best Practices

  • Always use ecosystem.config.js: Git-friendly and reproducible vs. bash commands.
  • Enable cluster mode (instances: 'max') to scale across CPUs without code changes.
  • Separate logs (error_file, out_file) and rotate with pm2 install pm2-logrotate.
  • Strict env vars: NODE_ENV=production + secrets via .env (dotenv).
  • Proactive monitoring: Run pm2 monit in screen/tmux, alert on restarts >0.

Common Errors to Avoid

  • Forgetting pm2 save and pm2 startup: Server reboot = dead apps.
  • No names (--name): pm2 list becomes chaotic with anonymous IDs.
  • Cluster without unique PORTs: Collisions; force per-instance PORTs if needed.
  • Unconfigured logs: Fills /var/log, silences app in production.

Next Steps

  • Official docs: PM2.io
  • PM2 Plus for web dashboards and Slack alerts.
  • Integrate with Docker: Use pm2-runtime in your Dockerfile.
  • Check our Learni trainings on Node.js Production and DevOps to master PM2 in a team.