Setting Up NVM, PM2, and Process Management on Linux

Khimananda Oli 3 min read DevOps
Setting Up NVM, PM2, and Process Management on Linux

Setting Up NVM, PM2, and Process Management on Linux

A production-ready foundation for running Node.js — Part 1 of 3

Don't apt install nodejs. You'll get a stale version pinned to your distro's release cycle, and switching between projects becomes a mess. NVM gives you per-user Node version management with zero system-level conflicts.

Install NVM

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

Reload your shell:

source ~/.bashrc

Verify:

nvm --version
# 0.40.1

Install Node.js

# Install LTS
nvm install --lts

# Or a specific version
nvm install 20.18.0

# Set default — without this, new shells may pick a different version
nvm alias default 20.18.0

# Verify
node -v && npm -v

Where Does Node Live?

NVM installs per-user. Your Node binary path looks like:

/home/deploy/.nvm/versions/node/v20.18.0/bin/node

This matters when PM2 needs to register a systemd service. Keep it in mind.

Install PM2

npm install -g pm2

Since we used NVM, PM2 lives at:

~/.nvm/versions/node/v20.18.0/bin/pm2

Quick sanity check:

pm2 --version

PM2 Startup — Survive Reboots

This is where NVM + PM2 gets tricky. PM2's startup script needs the full path to the Node binary because systemd doesn't load your shell environment.

Generate the startup script:

pm2 startup systemd

PM2 will print a command like:

sudo env PATH=$PATH:/home/deploy/.nvm/versions/node/v20.18.0/bin \
  /home/deploy/.nvm/versions/node/v20.18.0/lib/node_modules/pm2/bin/pm2 \
  startup systemd -u deploy --hp /home/deploy

Don't modify it. Copy and run that exact command.

Then save the current process list:

pm2 save

PM2 will now resurrect all managed apps on reboot.

The Stale dump.pm2 Trap

If PM2 keeps resurrecting old or dead processes after a restart, it's because pm2 save wrote a file with stale data.

Fix:

pm2 kill
rm -f ~/.pm2/dump.pm2
# start your apps again (covered in Part 2)
pm2 save

Always run pm2 save after any change to your process list. Forgetting this will bite you at 3 AM when the server reboots and your app doesn't come back.

PM2 Log Rotation

By default, PM2 logs grow unbounded. In production, that's a disk space incident waiting to happen.

pm2 install pm2-logrotate

pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 10
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss
pm2 set pm2-logrotate:workerInterval 30
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'

Verify:

pm2 conf pm2-logrotate

Option B: System logrotate

If you prefer the OS-level approach:

# /etc/logrotate.d/pm2-myapp
/home/deploy/.pm2/logs/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    copytruncate
}

Use copytruncate — PM2 holds the file handle open, so you can't move the log file out from under it.

Quick Reference — PM2 Commands

pm2 status           # list all managed processes
pm2 logs             # tail all logs
pm2 logs myapp       # tail specific app
pm2 monit            # real-time CPU/memory dashboard
pm2 reload myapp     # zero-downtime reload (cluster mode)
pm2 restart myapp    # hard restart
pm2 stop myapp       # stop without removing
pm2 delete myapp     # stop and remove from list
pm2 save             # persist process list
pm2 resurrect        # restore saved list

What We Have So Far

  • ✅ NVM installed with a pinned Node.js version
  • ✅ PM2 installed globally via NVM
  • ✅ Startup script registered — apps survive reboots
  • ✅ Log rotation configured — no disk space surprises

Next up — Part 2: Running Your Node.js Application with PM2

Follow me for Part 2, or check out more DevOps content on khimananda.com.