Starting a Node.js app with node server.js works until you close the SSH session, the app crashes, or the server reboots. PM2 is a process manager that keeps apps alive, restarts them on failure, starts them at boot, manages logs and can run one process per CPU core.

Install PM2

PM2 is an npm package. Install it globally (with Node.js already installed — see installing Node.js on Ubuntu):

npm install -g pm2
pm2 -v

With a NodeSource install you’ll need sudo for global packages, or set a user-level npm prefix.

Start an app

cd ~/myapp
pm2 start server.js --name myapp

Other kinds of apps:

pm2 start npm --name web -- start          # runs "npm start"
pm2 start "pnpm start" --name web
pm2 start app.py --interpreter python3      # PM2 can supervise non-Node programs too

Everyday commands

Command Purpose
pm2 ls List processes with status, CPU, memory, restarts
pm2 logs myapp Stream logs (--lines 200 for history)
pm2 monit Live terminal dashboard
pm2 show myapp Details: paths, env, uptime
pm2 restart myapp Hard restart
pm2 reload myapp Zero-downtime reload (cluster mode)
pm2 stop myapp Stop, keep in the list
pm2 delete myapp Remove from PM2
pm2 flush Empty log files

Start on boot

pm2 startup

PM2 prints a command beginning with sudo env PATH=… — copy and run it. It creates a systemd service for your user. Then save the current process list, which is what gets restored at boot:

pm2 save

Run pm2 save again whenever you add or remove apps. After upgrading Node.js, run pm2 update and redo pm2 startup so the service points at the new binary.

The ecosystem file

Command-line flags get unwieldy. Describe apps in ecosystem.config.js and commit it with the code:

module.exports = {
  apps: [
    {
      name: 'myapp',
      script: 'server.js',
      cwd: '/home/deploy/myapp',
      instances: 'max',          // one per CPU core
      exec_mode: 'cluster',
      max_memory_restart: '400M',
      env: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
    },
    {
      name: 'worker',
      script: 'worker.js',
      instances: 1,
      cron_restart: '0 4 * * *', // daily restart at 04:00
    },
  ],
};
pm2 start ecosystem.config.js
pm2 reload ecosystem.config.js --update-env

--update-env is required for changed environment variables to take effect — a frequent source of confusion.

Cluster mode

Node runs JavaScript on one thread. In cluster mode PM2 starts several instances and load-balances incoming connections across them, using every CPU core:

pm2 start server.js -i max

Things to know:

  • Your app must be stateless between requests: sessions, caches and WebSocket state belong in Redis or a database, not process memory.
  • pm2 reload restarts instances one at a time, so the app stays available during deploys.
  • On a 1–2 core VPS, cluster mode adds little; the gain comes on 4+ cores.

Logs and rotation

PM2 writes logs to ~/.pm2/logs/<name>-out.log and -error.log. They grow without limit, so install the rotation module:

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 20M
pm2 set pm2-logrotate:retain 10
pm2 set pm2-logrotate:compress true

Memory limits and restarts

max_memory_restart: '400M' restarts an instance that exceeds the limit — a safety net for slow memory leaks, not a fix. Watch the restart counter in pm2 ls; a climbing number means something is wrong. Investigate with pm2 logs and pm2 show.

Deploying updates

A simple deploy script:

cd /home/deploy/myapp
git pull --ff-only
npm ci --omit=dev
npm run build --if-present
pm2 reload ecosystem.config.js --update-env

Put Nginx in front

Bind the app to localhost (HOST=127.0.0.1) and let Nginx terminate HTTPS and serve static files:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Full setup: Node.js on a VPS with Nginx.

PM2 or systemd?

systemd is built into Linux and needs no extra software; PM2 adds cluster mode, zero-downtime reloads and friendlier log handling. For a single small service, a systemd unit is enough. For several Node apps, PM2 is more convenient. Either way, avoid running the app as root.

Cluster mode shines with more cores: our KVM VPS plans scale from 1 to 14 vCPU cores, so you can grow without changing your setup.

  • Node.js
  • PM2
  • Deployment