Tutorials

How to Deploy a Node.js Express App on a Linux VPS with Nginx & PM2

How to Deploy Node.js Express App on Linux VPS with Nginx & PM2 - CpanelFree Guide
Written by Blog

Quick Answer: To deploy a production Node.js Express app on a Linux VPS: 1) Install Node.js LTS via NodeSource repository, 2) Use PM2 (pm2 start server.js -i max --name app) for background daemon management and automatic restarts, 3) Configure Nginx as a reverse proxy passing traffic from port 80/443 to http://127.0.0.1:3000, and 4) Secure the domain with a free Let’s Encrypt SSL certificate using Certbot.

Step 1: Install Node.js LTS on Ubuntu 24.04

# Install Node.js v20/v22 LTS
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs build-essential
node -v && npm -v

Step 2: Deploy App and Configure PM2 Process Manager

# Install PM2 globally
sudo npm install -g pm2

# Clone and launch application in Cluster Mode
cd /var/www/my-node-app
npm install --production
pm2 start server.js -i max --name "node-app"

# Enable automatic startup on system boot
pm2 startup systemd
pm2 save

Step 3: Configure Nginx as Reverse Proxy

Create a new Nginx server block at /etc/nginx/sites-available/yourdomain.com:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1: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;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
# Test and reload Nginx
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Step 4: Issue Free SSL Certificate with Certbot

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Zero-Downtime Reloads with PM2 Ecosystem Config

Create an ecosystem.config.js file in your project root to manage multi-environment deployment configurations:

module.exports = {
  apps: [{
    name: 'api-cluster',
    script: './server.js',
    instances: 'max',
    exec_mode: 'cluster',
    env_production: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};
# Execute zero-downtime cluster reload
pm2 reload ecosystem.config.js --env production

Securing Node.js with Helmet and Rate Limiting Middleware

In production Express applications, always include helmet to set secure HTTP headers and express-rate-limit to prevent brute-force API abuse:

const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
app.use(helmet());
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));

Production Performance Checklist for Node.js on Linux VPS

  • Enable Cluster Mode: Launch with pm2 start server.js -i max to spin up a worker thread for each available CPU core.
  • Configure Memory Thresholds: Set --max-memory-restart 500M in PM2 to automatically recycle workers that develop memory leaks before server RAM is exhausted.
  • Implement Nginx Gzip Compression: Enable Gzip and Brotli compression in Nginx to reduce JSON response payload size by up to 75%.
  • Enforce HTTPS & HTTP/2: Terminate SSL at Nginx using modern TLS 1.3 cipher suites and HTTP/2 multiplexing.

How do I monitor PM2 application performance in real time?

Run pm2 monit in your terminal. This opens an interactive terminal dashboard displaying real-time CPU utilization, memory consumption, request logs, and event loop latency across all active worker threads.

Deploy Full-Stack Apps on CpanelFree Cloud

Deploy high-performance Node.js, Python, and PHP web applications with root SSH access on CpanelFree.

Deploy Cloud Server

Frequently Asked Questions

Why use PM2 instead of running node server.js directly?

Running node server.js in a terminal terminates if you close the SSH session or if an unhandled JavaScript exception crashes the process. PM2 runs the app in the background, automatically restarts it on crashes, and balances load across CPU cores.

Configuring Automated PM2 System Log Rotation

In high-throughput Node.js production environments, standard console.log output can consume gigabytes of disk space. Install pm2-logrotate to automatically compress and rotate log files:

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7

Pro Sysadmin Tip: Tuning Node.js Memory Limits on Linux VPS

By default, Node.js caps heap memory allocation to ~1.4GB on 64-bit systems. To allow intensive data processing apps to utilize full VPS RAM, pass --max-old-space-size=4096 in your PM2 start command.

Deploying Node.js Express behind Nginx with PM2 cluster management provides an enterprise-grade foundation for high-concurrency modern web applications.

Configuring Automated PM2 System Boot Recovery on Linux

To ensure that all Node.js worker clusters reboot automatically after server restarts or cloud maintenance events, execute pm2 startup systemd, copy the generated command into your root terminal, and run pm2 save to freeze the active process list in system storage.

How do I handle environment variables securely in PM2?

Declare environment variables inside ecosystem.config.js or use a .env file parsed by dotenv, ensuring sensitive API secrets and database passwords are never hardcoded in git repositories.

Pairing PM2 cluster mode with Nginx caching buffers offloads repetitive socket handshakes, delivering blazing sub-20ms API response times across production cloud servers.

Integrating PM2 with automated GitHub Actions CI/CD deployment pipelines enables seamless zero-downtime code updates across your production cloud infrastructure.

About the author

Blog

DevOps architect and Linux sysadmin specializing in server hardening, OpenLiteSpeed performance optimization, and free cloud hosting infrastructure.

Leave a Comment