
Node.js is a JavaScript runtime that lets you run server-side code — perfect for building APIs, web apps, real-time applications, and microservices. PM2 is a production-grade Process Manager that keeps your Node.js app running as a background daemon, automatically restarts it on crash, and launches it on every server reboot. This guide walks through the complete setup from a bare Ubuntu VPS to a running, secured application.
System Requirements
- Ubuntu 20.04, 22.04, or 24.04 LTS VPS
- At least 512 MB RAM (1 GB recommended)
- Root or sudo-enabled user access
- SSH access to your VPS (see SSH Guide)
Step 1: Install Node.js via NVM
Using NVM (Node Version Manager) is strongly recommended over apt install nodejs. NVM lets you manage multiple Node.js versions, switch between them instantly, and install packages globally without sudo.
Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bashReload your shell so the nvm command becomes available:
source ~/.bashrcVerify the installation:
nvm --versionInstall the LTS Release of Node.js
nvm install --lts
nvm use --lts
nvm alias default nodeConfirm the installed versions:
node -v
npm -vWhy LTS? LTS (Long-Term Support) releases receive security patches and bug fixes for over 30 months, making them the right choice for production servers. Current releases are for trying out new features, not running live applications.
Step 2: Create a Test Node.js Application
Create a project folder and scaffold a minimal Express app:
mkdir -p ~/myapp && cd ~/myapp
npm init -y
npm install expressCreate the entry file app.js:
cat > app.js << 'EOF'
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({ message: 'Hello from AsiaGB VPS!', status: 'ok' });
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
EOFTest it directly before adding PM2:
node app.jsOpen a second terminal and run: curl http://localhost:3000 — you should see the JSON response. Press Ctrl+C to stop.
Step 3: Install and Configure PM2
PM2 acts as a production Process Manager — it keeps your app running as a background service, restarts it on crash, and survives server reboots.
npm install -g pm2Start Your App with PM2
cd ~/myapp
pm2 start app.js --name "myapp"Check the process status and live logs:
pm2 status
pm2 logs myappEnable Auto-start on Reboot
pm2 startup
# Copy the command PM2 prints and run it, e.g.:
# sudo env PATH=$PATH:/home/user/.nvm/versions/node/v20.x.x/bin pm2 startup systemd -u user --hp /home/user
pm2 saveUseful PM2 Commands: pm2 restart myapp — restart · pm2 stop myapp — stop · pm2 delete myapp — remove from PM2 · pm2 monit — real-time CPU/memory dashboard
Step 4: Set Up Nginx as a Reverse Proxy
Node.js runs on port 3000 internally, but visitors connect on port 80 (HTTP) or 443 (HTTPS). Nginx bridges the two — it accepts public traffic and forwards it to Node.js.
Install Nginx
apt update && apt install nginx -y
ufw allow 'Nginx Full'Create a Server Block
nano /etc/nginx/sites-available/myappAdd the following config (replace yourdomain.com with your real domain):
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_cache_bypass $http_upgrade;
}
}Enable the site and reload Nginx:
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginxVisit http://yourdomain.com — you should see the JSON from your Node.js app.
Step 5: Install SSL with Certbot (Recommended)
Add Let's Encrypt SSL so your app serves HTTPS traffic:
apt install certbot python3-certbot-nginx -y
certbot --nginx -d yourdomain.com -d www.yourdomain.comCertbot automatically updates your Nginx config for HTTPS and sets up auto-renewal — no manual steps needed.
Install Node.js via NodeSource (Alternative to NVM)
If your VPS is multi-user, or you have several services that should all share one system-wide Node.js version, installing from the NodeSource repository is often simpler than NVM. The Node.js binary lands in the global path for every user and works with systemd out of the box, without any extra PATH configuration. This makes it a predictable choice for production servers that value simplicity.
Add the NodeSource repo for Node.js 20 LTS and install it:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v
npm -vIf you need to compile native modules (such as bcrypt or sharp) from source, install the build toolchain as well:
sudo apt install -y build-essentialNVM or NodeSource? Use NVM when you switch between versions frequently or develop on a single machine. Use NodeSource when you want one system-wide Node.js for production so systemd and PM2 can locate the binary easily. For a typical AsiaGB VPS running one app per server, NodeSource is usually the lower-maintenance option.
Run Your App in PM2 Cluster Mode
By default, Node.js runs on a single CPU core, leaving the rest of your VPS underutilized. PM2's cluster mode spreads incoming requests across multiple worker processes — one per core — using Node.js's built-in cluster module. You get higher throughput and zero-downtime reloads in one feature.
Start a cluster with the instance count set to max (equal to the number of CPU cores):
pm2 start app.js --name "myapp" -i maxOr pin a fixed number of workers, e.g. 2 processes (ideal for a 2-core VPS):
pm2 start app.js --name "myapp" -i 2Rather than typing flags every time, create an ecosystem.config.js file to keep all your configuration in one place — environment variables, instance count, and a memory limit:
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'myapp',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
max_memory_restart: '300M',
env: {
NODE_ENV: 'production',
PORT: 3000
}
}]
};
EOFThen launch everything with a single file:
pm2 start ecosystem.config.js
pm2 saveThe max_memory_restart option restarts a worker automatically when it exceeds the set RAM threshold — a simple guard against memory leaks crashing your app on a RAM-constrained VPS.
Cluster mode caveat: If your app stores state in memory (for example in-memory sessions), each worker has its own isolated copy and they will not share data. Move sessions to an external store such as Redis or a database instead.
Monitoring, Logs, and Zero-Downtime Reload
Once your app is in production, the priorities become watching process health and deploying new code without serving errors to users. PM2 ships with the tooling for both.
Watch Resources and Logs in Real Time
pm2 monit # live CPU/memory dashboard per process
pm2 logs myapp # stream live logs (stdout + stderr)
pm2 logs myapp --lines 200 # show the last 200 log lines
pm2 show myapp # process details (uptime, restarts, path)Rotate Logs So They Don't Fill the Disk
PM2 logs grow indefinitely and can eventually fill your VPS disk. Install the pm2-logrotate module to rotate and compress logs automatically:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7The settings above cap each file at 10 MB and keep the last 7 files.
Zero-Downtime Reload After Code Updates
The restart command stops every process at once, causing a brief outage. In cluster mode, use reload instead — PM2 replaces workers one at a time while the others keep serving requests, so users never see an error:
git pull
npm install --production
pm2 reload myappWith pm2 startup and pm2 save already configured, your app relaunches on every VPS reboot, helping your service meet the 99% uptime standard offered on AsiaGB VPS.
Tip: Expose a health-check endpoint such as /healthz in your app and have an external monitor (like Uptime Kuma) ping it every minute. You'll know the moment something breaks — before your users report it.
Frequently Asked Questions
How is PM2 different from systemd — which should I use?
systemd is Ubuntu's init system, managing OS-level services, while PM2 is a Node.js-specific process manager that bundles cluster mode, log management, and zero-downtime reload. In practice you use both together: pm2 startup generates a systemd unit that launches PM2 at boot, and PM2 then supervises your Node.js processes.
How much VPS RAM do I need to run Node.js + PM2 comfortably?
A small Express app runs fine on a 512 MB VPS, but if you enable cluster mode with several workers or handle high traffic, 1 GB or more is recommended. AsiaGB Linux VPS starts at ฿500/month on Ubuntu with full root access, and you can upgrade RAM/CPU as your app grows.
My app runs with node app.js but returns 502 Bad Gateway through Nginx — why?
This usually has two causes: PM2 is not actually running the app (check with pm2 status that it shows online), or the port in Nginx's proxy_pass doesn't match the port the app listens on. Confirm both are 3000, then run nginx -t and systemctl reload nginx.
I deployed new code but PM2 still runs the old version — what now?
PM2 does not reload files automatically after a git pull. Run pm2 reload myapp (cluster mode) or pm2 restart myapp to load the new code. If you changed values in ecosystem.config.js, use pm2 reload ecosystem.config.js --update-env so the new environment variables take effect.
Summary
- Install Node.js via NVM for easy version management
- Use PM2 to run your app as a production daemon with auto-restart
- Run pm2 startup && pm2 save to survive server reboots
- Use Nginx Reverse Proxy to route port 80/443 traffic to Node.js
- Add SSL via Certbot to enable HTTPS with automatic renewal
Need a VPS for Node.js?
AsiaGB Linux VPS starts at ฿500/month · Full Root Access · SSD Storage · Unlimited Bandwidth · Choose any Ubuntu version
View VPS Plans