Tools: Setting Up NVM, PM2, and Process Management on Linux

Tools: Setting Up NVM, PM2, and Process Management on Linux

Why NVM Over System Node?

Install NVM

Install Node.js

Where Does Node Live?

Install PM2

PM2 Startup — Survive Reboots

The Stale dump.pm2 Trap

PM2 Log Rotation

Option A: pm2-logrotate (Recommended)

Option B: System logrotate

Quick Reference — PM2 Commands

What We Have So Far Don't apt install nodejs. You'll get a stale version pinned to your distro's release cycle, and switching between projects that need different Node versions becomes a mess. NVM gives you per-user Node version management with zero system-level conflicts. NVM installs per-user. Your Node binary path looks like: This matters when PM2 needs to register a systemd service. Keep it in mind. Since we used NVM, PM2 lives at: This is where NVM + PM2 gets tricky. PM2's startup script needs the full path to the Node binary because systemd runs non-interactively — NVM isn't loaded in that context. Generate the startup script: PM2 will print a command like: Copy and run that exact command. Don't modify it. Then save the current process list: PM2 will now resurrect all managed apps on reboot. If PM2 keeps resurrecting old or dead processes after a restart, it's because pm2 save wrote a dump.pm2 file with stale state. 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. By default, PM2 logs grow unbounded. In production, that's a disk space incident waiting to happen. If you prefer the OS-level approach: Use copytruncate — PM2 holds the file handle open, so you can't move the log file out from under it. Follow for Part 2 — Running Your Node.js Application with PM2 Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 500;">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/-weight: 500;">install.sh | bash -weight: 500;">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/-weight: 500;">install.sh | bash -weight: 500;">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/-weight: 500;">install.sh | bash source ~/.bashrc source ~/.bashrc source ~/.bashrc nvm --version # 0.40.1 nvm --version # 0.40.1 nvm --version # 0.40.1 # Install LTS nvm -weight: 500;">install --lts # Or a specific version nvm -weight: 500;">install 20.18.0 # Set default — without this, new shells may pick a different version nvm alias default 20.18.0 # Verify node -v && -weight: 500;">npm -v # Install LTS nvm -weight: 500;">install --lts # Or a specific version nvm -weight: 500;">install 20.18.0 # Set default — without this, new shells may pick a different version nvm alias default 20.18.0 # Verify node -v && -weight: 500;">npm -v # Install LTS nvm -weight: 500;">install --lts # Or a specific version nvm -weight: 500;">install 20.18.0 # Set default — without this, new shells may pick a different version nvm alias default 20.18.0 # Verify node -v && -weight: 500;">npm -v /home/deploy/.nvm/versions/node/v20.18.0/bin/node /home/deploy/.nvm/versions/node/v20.18.0/bin/node /home/deploy/.nvm/versions/node/v20.18.0/bin/node -weight: 500;">npm -weight: 500;">install -g pm2 -weight: 500;">npm -weight: 500;">install -g pm2 -weight: 500;">npm -weight: 500;">install -g pm2 ~/.nvm/versions/node/v20.18.0/bin/pm2 ~/.nvm/versions/node/v20.18.0/bin/pm2 ~/.nvm/versions/node/v20.18.0/bin/pm2 pm2 --version pm2 --version pm2 --version pm2 startup systemd pm2 startup systemd pm2 startup systemd -weight: 600;">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 -weight: 600;">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 -weight: 600;">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 pm2 kill rm -f ~/.pm2/dump.pm2 # -weight: 500;">start your apps again (covered in Part 2) pm2 save pm2 kill rm -f ~/.pm2/dump.pm2 # -weight: 500;">start your apps again (covered in Part 2) pm2 save pm2 kill rm -f ~/.pm2/dump.pm2 # -weight: 500;">start your apps again (covered in Part 2) pm2 save pm2 -weight: 500;">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 * * *' pm2 -weight: 500;">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 * * *' pm2 -weight: 500;">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 * * *' pm2 conf pm2-logrotate pm2 conf pm2-logrotate pm2 conf pm2-logrotate # /etc/logrotate.d/pm2-myapp /home/deploy/.pm2/logs/*.log { daily missingok rotate 14 compress delaycompress notifempty copytruncate } # /etc/logrotate.d/pm2-myapp /home/deploy/.pm2/logs/*.log { daily missingok rotate 14 compress delaycompress notifempty copytruncate } # /etc/logrotate.d/pm2-myapp /home/deploy/.pm2/logs/*.log { daily missingok rotate 14 compress delaycompress notifempty copytruncate } pm2 -weight: 500;">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 -weight: 500;">restart myapp # hard -weight: 500;">restart pm2 -weight: 500;">stop myapp # -weight: 500;">stop without removing pm2 delete myapp # -weight: 500;">stop and -weight: 500;">remove from list pm2 save # persist process list pm2 resurrect # restore saved list pm2 -weight: 500;">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 -weight: 500;">restart myapp # hard -weight: 500;">restart pm2 -weight: 500;">stop myapp # -weight: 500;">stop without removing pm2 delete myapp # -weight: 500;">stop and -weight: 500;">remove from list pm2 save # persist process list pm2 resurrect # restore saved list pm2 -weight: 500;">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 -weight: 500;">restart myapp # hard -weight: 500;">restart pm2 -weight: 500;">stop myapp # -weight: 500;">stop without removing pm2 delete myapp # -weight: 500;">stop and -weight: 500;">remove from list pm2 save # persist process list pm2 resurrect # restore saved list - ✅ 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