Deploying Node.js Application on VPS with PM2
In this guide, we'll cover the process of deploying a Node.js application on a VPS server using the PM2 process manager to ensure reliability, automatic restart, and application scaling.
What is PM2 and why you should use it
PM2 (Process Manager 2) is an advanced process manager for Node.js applications that provides:
- Automatic restart when an application crashes
- Real-time process monitoring
- Log management
- Automatic startup on system boot
Prerequisites
- VPS server with Linux (Ubuntu 24 is used as an example)
- Root access
- Basic knowledge of Linux command line
- Node.js project ready for deployment
Installing Node.js and npm
First, let's install Node.js and npm using NVM (Node Version Manager), which allows you to easily switch between different Node.js versions.
# Install curl if you don't have it
sudo apt update
sudo apt install curl
# Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.6/install.sh | bash
source ~/.bashrc
# Check if NVM was successfully installed
nvm --version
# Install the latest LTS version of Node.js
nvm install --lts
# Check Node.js and npm versions
node --version
npm --versionTIP
It's recommended to use LTS (Long Term Support) versions of Node.js for mission-critical applications, as these versions provide long-term support and stability.
Installing PM2
Let's install PM2 globally for convenience:
npm install -g pm2Preparing the Node.js Application
Cloning the project from a repository
Clone your project from a Git repository or upload it to the server manually:
# Clone the project
git clone https://github.com/username/your-project.git
cd your-project
# Install dependencies
npm installConfiguring and launching the application with PM2
Basic launch
The simplest way to start an application with PM2:
pm2 start app.js --name "my-app"Where my-app is the name of your application, which you'll use to refer to PM2 for further management.
Creating a PM2 configuration file
For more detailed configuration, let's create an ecosystem.config.js file:
cat > ecosystem.config.js << 'EOL'
module.exports = {
apps: [{
name: "my-app",
script: "app.js",
autorestart: true, // restart automatically if the process exits
watch: false,
max_memory_restart: "300M", // restart the process if it exceeds this memory limit
env: {
NODE_ENV: "development",
},
env_production: {
NODE_ENV: "production",
PORT: 3000
}
}]
}
EOLESM projects
If your package.json contains "type": "module", PM2 cannot load a .js config file (ERR_REQUIRE_ESM). Name the file ecosystem.config.cjs instead and reference it explicitly (pm2 start ecosystem.config.cjs), since PM2's auto-discovery only looks for the .js name.
Starting the application using the configuration file:
pm2 start ecosystem.config.js --env productionConfiguring PM2 autostart on server reboot
To make your application start automatically when the server reboots, run the following command:
pm2 startupExecute the command that PM2 displays in the console.
Then save the current list of processes:
pm2 saveNow your application(s) will start even after server reboot without manual intervention.
Avoid running your app as root
pm2 startup generates a systemd service tied to the current user. For security, run your application under a dedicated non-root user rather than root, and run pm2 startup while logged in as that user so the service and its permissions are scoped correctly.
Setting up Nginx as a reverse proxy
To provide public access to your application (for example, through a domain), you need to configure Nginx:
# Install Nginx
sudo apt install nginx
# Create a site configuration
sudo nano /etc/nginx/sites-available/my-appAdd the following configuration:
server {
listen 80;
server_name your-domain.com www.your-domain.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_cache_bypass $http_upgrade;
}
}Activate the configuration and restart Nginx:
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginxTIP
After configuring Nginx, it's recommended to set up an SSL certificate using Let's Encrypt for secure connections. Instructions can be found in our Let's Encrypt setup article.
Basic PM2 commands
Process monitoring
# Real-time monitoring
pm2 monit
# View the process list
pm2 list
# Detailed information about a process
pm2 show my-appProcess management
# Restart the application
pm2 restart my-app
# Stop the application
pm2 stop my-app
# Remove the process from the PM2 list
pm2 delete my-appWorking with logs
# View general logs from all applications
pm2 logs
# View logs for a specific application
pm2 logs my-app
# View the last 200 lines of logs
pm2 logs --lines 200Updating the application
To deploy a new version, pull the latest code, install any new dependencies, and restart the process:
# Pull the new code and install dependencies
git pull
npm install
# Restart the application
pm2 restart my-appRestart causes a brief interruption
pm2 restart stops the process and starts it again, so there is a short window (typically well under a second) during which requests are not served. For most applications this is acceptable. If you need to survive that window, put a load balancer in front of two or more separate servers and update them one at a time, rather than relying on PM2 alone.
Monitoring and statistics
PM2 provides basic free monitoring, but for mission-critical applications, it's recommended to set up more advanced monitoring:
PM2 Plus (paid solution)
pm2 plusIntegration with Prometheus + Grafana
Metrics can be exported to Prometheus with a PM2 module such as pm2-metrics. PM2 modules are installed with pm2 install — there is no separate global npm install step:
pm2 install pm2-metricsThe module starts an HTTP server that exposes PM2 metrics at http://<host>:9209/metrics. Point Prometheus at that target:
# prometheus.yml
scrape_configs:
- job_name: pm2
static_configs:
- targets: ['localhost:9209']Then use Grafana to visualize the collected data. Alternative community modules (e.g. pm2-prom-module) expose metrics on their own ports — check the module's documentation for the exact endpoint.
Troubleshooting
Application won't start
Check the logs:
pm2 logs my-appCommon causes of problems:
- Errors in the application code
- Port already in use
- Insufficient file access permissions
- Dependencies not installed
Application crashes under high load
- Check memory usage:
pm2 monit - Consider increasing the memory limit in the configuration
- Consider scaling the application across multiple servers
- Upgrade your server plan if resources are insufficient
Problem: PM2 doesn't restart after server reboot
Reconfigure autostart:
pm2 unstartup
pm2 startup
pm2 saveConclusion
Proper configuration of a Node.js application with PM2 on a VPS ensures reliability, fault tolerance, and ease of management.
PM2 provides many tools for monitoring and managing processes, making it indispensable for your projects.
For additional help setting up your Node.js application on our VPS, contact our support team.