All posts
PM2 Node.js process manager dashboard with terminal
  • PM2
  • Node.js
  • Deployment
  • Process Management
  • VPS

PM2 Complete Guide — Deploy & Manage Node.js Apps in Production

Everything you need to run Node.js apps with PM2 — start, stop, monitor, cluster mode, log management, ecosystem.config.js, startup scripts, and zero-downtime reloads.

5 min read

PM2 is the standard process manager for Node.js applications in production. It keeps your app running after crashes, restarts it on server reboots, provides built-in log management, and supports cluster mode for multi-core performance. This guide covers everything you need for a production setup.

Installation

Install PM2 globally with npm:

sudo npm install -g pm2

Verify the installation:

pm2 --version

Starting Applications

Start a script directly

pm2 start app.js --name "my-app"

Start an npm script

pm2 start npm --name "my-app" -- start

Start a specific entry point

For Astro (Node adapter), Next.js standalone, or any custom server file:

# Astro (Node adapter)
pm2 start dist/server/entry.mjs --name "astro-app"

# Next.js standalone
pm2 start .next/standalone/server.js --name "next-app"

# Custom Express server
pm2 start server.js --name "api-server"

For a complete Astro + Nginx + PM2 setup, see Deploying Astro on a VPS with Nginx. For Next.js deployments, see Deploying Next.js with Nginx or Deploying Next.js with Apache2.

Pass environment variables at start

pm2 start app.js --name "my-app" --env production
# or explicitly:
PORT=8080 NODE_ENV=production pm2 start app.js --name "my-app"

Core Process Commands

pm2 list                    # List all running processes
pm2 status                  # Same as pm2 list
pm2 show my-app             # Detailed info for one process

pm2 stop my-app             # Stop (keeps in list)
pm2 start my-app            # Start a stopped process
pm2 restart my-app          # Restart (brief downtime)
pm2 reload my-app           # Zero-downtime restart (cluster mode)
pm2 delete my-app           # Remove from list entirely

pm2 stop all                # Stop all processes
pm2 restart all             # Restart all processes
pm2 delete all              # Remove all processes

Processes can be referenced by name or by their numeric ID shown in pm2 list.

To remove a process from PM2 entirely (stop + delete from the list), use pm2 delete:

pm2 delete my-app     # remove one process by name or ID
pm2 delete all        # remove all processes

After deleting, run pm2 save to update the saved list so the process doesn’t come back after a reboot.


Surviving Server Reboots

This is the most important step for production. Without it, your app won’t restart after a reboot.

1. Generate a startup script

pm2 startup

PM2 prints a command tailored to your system. Copy and run it — it looks like:

sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu

2. Save the current process list

pm2 save

This saves the list of running processes to ~/.pm2/dump.pm2. On reboot, PM2 restores them automatically.

Run pm2 save again whenever you add, remove, or change a process.

3. Restore a saved process list

If you need to manually load the saved list back — for example after a pm2 kill or on a fresh login — run:

pm2 resurrect

This reloads all processes from ~/.pm2/dump.pm2, starting them exactly as they were when you last ran pm2 save.

4. Verify startup is registered

pm2 ls          # confirm processes are listed
sudo systemctl status pm2-$(whoami)

Ecosystem Configuration File

For anything beyond a simple one-liner, use an ecosystem file. It keeps all your PM2 configuration in version control.

Create ecosystem.config.cjs in your project root (next to package.json). PM2 also stores its internal state — the saved process list and logs — under ~/.pm2/:

  • ~/.pm2/dump.pm2 — saved process list (pm2 save / pm2 resurrect)
  • ~/.pm2/logs/ — per-process log files
  • ~/.pm2/pm2.log — PM2 daemon log
// ecosystem.config.cjs
module.exports = {
  apps: [
    {
      name: 'my-app',
      script: 'dist/server/entry.mjs',

      // Number of instances — 'max' uses all CPU cores
      instances: 1,

      // Restart automatically on crash
      autorestart: true,

      // Watch files and restart on changes (use in dev only)
      watch: false,

      // Memory limit — restart if exceeded
      max_memory_restart: '512M',

      // Environment variables per mode
      env: {
        NODE_ENV: 'development',
        PORT: 4321,
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 4321,
      },
    },
  ],
};

Start with the ecosystem file:

# Development environment
pm2 start ecosystem.config.cjs

# Production environment
pm2 start ecosystem.config.cjs --env production

Other commands work the same way:

pm2 restart ecosystem.config.cjs --env production
pm2 reload ecosystem.config.cjs --env production   # zero-downtime
pm2 stop ecosystem.config.cjs
pm2 delete ecosystem.config.cjs

Cluster Mode

Cluster mode spawns one process per CPU core and load-balances incoming connections across them. This is the fastest way to use all cores on a multi-core VPS without changing your app code.

// ecosystem.config.cjs
module.exports = {
  apps: [
    {
      name: 'my-app',
      script: 'server.js',
      instances: 'max',     // one per CPU core
      exec_mode: 'cluster', // required for load balancing
      autorestart: true,
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
    },
  ],
};

In cluster mode, use pm2 reload instead of pm2 restart to achieve zero-downtime updates — PM2 cycles workers one at a time.

Caution: Cluster mode requires your app to be stateless. In-memory session storage, WebSocket connections, and anything stored in a global variable will not be shared across workers. Use Redis or a database for shared state.


Log Management

View logs

pm2 logs                  # Stream all logs
pm2 logs my-app           # Stream logs for one app
pm2 logs my-app --lines 100  # Show last 100 lines

Log file locations

Logs are stored in ~/.pm2/logs/:

~/.pm2/logs/my-app-out.log   # stdout
~/.pm2/logs/my-app-error.log # stderr

Flush logs

pm2 flush            # Delete all log files
pm2 flush my-app     # Delete logs for one app

Log rotation

Install the PM2 log rotation module to avoid logs filling up the disk:

pm2 install pm2-logrotate

Configure it:

pm2 set pm2-logrotate:max_size 50M      # rotate when file exceeds 50 MB
pm2 set pm2-logrotate:retain 14         # keep 14 rotated files
pm2 set pm2-logrotate:compress true     # gzip rotated files
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss

Monitoring

Built-in terminal dashboard

pm2 monit

Shows CPU and memory usage per process in real time.

Process metrics

pm2 show my-app

Displays uptime, restart count, memory, CPU, log paths, and more.

Web dashboard (PM2 Plus)

PM2 offers a hosted web dashboard at app.pm2.io. Free tier available. Connect with:

pm2 link <secret_key> <public_key>

Deployment Workflow

A typical deployment after a code update:

# 1. Pull latest code on server
git pull

# 2. Install any new dependencies
npm install --omit=dev

# 3. Rebuild (if applicable)
npm run build

# 4. Reload the process (zero-downtime in cluster mode, brief restart otherwise)
pm2 reload my-app

# 5. Save the process list if anything changed
pm2 save

Watch Mode (Development Only)

PM2 can watch files and restart the process automatically on changes. Never use this in production — it restarts on every file modification and causes excessive CPU usage.

pm2 start app.js --name "dev-app" --watch
pm2 start app.js --name "dev-app" --watch --ignore-watch="node_modules logs"

Important Notes

  • Always run pm2 save after adding or changing processes, so the state persists across reboots.
  • In cluster mode, use pm2 reload not pm2 restart to achieve zero-downtime updates.
  • Avoid --watch in production — it is for development only.
  • Store your ecosystem.config.cjs in version control so your PM2 setup is reproducible.
  • Use pm2-logrotate on any long-running server to prevent logs from filling your disk.

Frequently Asked Questions

What’s the difference between pm2 restart and pm2 reload?

restart kills the process and starts a new one — there is a brief moment of downtime. reload (cluster mode only) replaces workers one at a time while the others continue handling requests, achieving zero downtime.

My app isn’t restarting after a server reboot. What’s wrong?

You likely skipped one of the two steps: running the pm2 startup command (including the sudo env ... command it prints) and running pm2 save. Both are required.

How do I run multiple apps on the same server?

List them all in the apps array of your ecosystem file, or start each one separately with a unique --name. Give each app a different port and create a separate Nginx server block for each domain.

How do I set environment variables securely?

Don’t store secrets in ecosystem.config.cjs if it’s in version control. Instead, set them in a .env file on the server and load them at startup, or export them in the shell before running PM2. Alternatively use the env_production block in the ecosystem file for non-sensitive config only.

Can PM2 manage non-Node.js processes?

Yes — PM2 can manage any executable. Use interpreter: 'python3' or interpreter: 'none' in the ecosystem file. However, PM2 was designed for Node.js and its language-specific features (cluster mode, --harmony flags, etc.) only apply to Node.js processes.

How do I completely uninstall PM2?

pm2 delete all
pm2 unstartup
sudo npm uninstall -g pm2
rm -rf ~/.pm2