Skip to main content
Back to Category

Deploy Node.js App: PM2 and Nginx Guide

Complete guide to deploying Node.js applications on a VPS. Node.js installation with nvm, PM2 process manager, Nginx reverse proxy, SSL certificate, and environment variables management.

Read time: 15 min Web Hosting / Applications
nodejspm2nginxdeployvpsjavascriptreverse proxyssl

Table of Contents

Deploy Node.js Application: PM2 and Nginx Guide

Deploying Node.js applications to a production environment is a systematic process with the right tools and configuration. This guide covers everything from Node.js installation with nvm to PM2 process manager, Nginx reverse proxy configuration, and SSL certificate setup.

Node.js Installation (with nvm)

nvm (Node Version Manager) lets you manage multiple Node.js versions and is recommended for production environments.

hljs bash
# Download and run the nvm install script
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# Reload shell
source ~/.bashrc

# Check nvm version
nvm --version

# Install latest LTS version
nvm install --lts

# Install specific version
nvm install 20.11.0

# Set default version
nvm use --lts
nvm alias default node

# Check Node.js and npm versions
node --version
npm --version

Always use the LTS (Long Term Support) version in production. LTS versions receive security updates for a longer period.

Transferring the Application to the Server

Cloning with Git

hljs bash
# Create application directory
sudo mkdir -p /var/www/myapp
sudo chown $USER:$USER /var/www/myapp

# Clone the repo
cd /var/www
git clone https://github.com/user/myapp.git
cd myapp

# Install dependencies (production)
npm install --production

# Or install all dependencies
npm install

Environment Variables (.env)

hljs bash
# Create .env file
nano /var/www/myapp/.env
hljs env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
JWT_SECRET=your-secret-key-here
REDIS_URL=redis://localhost:6379
hljs bash
# Secure the .env file
chmod 600 /var/www/myapp/.env

Never add the .env file to your Git repository. Add .env to your .gitignore file.

PM2 Process Manager

PM2 is the most popular tool for managing Node.js applications in production. It offers automatic restarts, log management, and cluster mode.

Installing PM2

hljs bash
# Install PM2 globally
npm install -g pm2

# Check PM2 version
pm2 --version

Starting the Application with PM2

hljs bash
# Simple start
pm2 start app.js --name myapp

# With specific port
pm2 start app.js --name myapp -- --port 3000

# Using npm start
pm2 start npm --name myapp -- start

# Cluster mode (instances equal to CPU cores)
pm2 start app.js --name myapp -i max

# Specific number of instances
pm2 start app.js --name myapp -i 4

PM2 Ecosystem File

Use an ecosystem.config.js file for more advanced configuration:

hljs bash
nano /var/www/myapp/ecosystem.config.js
hljs javascript
module.exports = {
  apps: [
    {
      name: 'myapp',
      script: './app.js',
      instances: 'max',
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'development',
        PORT: 3000
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000
      },
      error_file: '/var/log/pm2/myapp-error.log',
      out_file: '/var/log/pm2/myapp-out.log',
      log_date_format: 'YYYY-MM-DD HH:mm:ss',
      max_memory_restart: '500M',
      watch: false,
      autorestart: true
    }
  ]
};
hljs bash
# Start with ecosystem file
pm2 start ecosystem.config.js --env production

# PM2 basic commands
pm2 list              # List running apps
pm2 status            # Status summary
pm2 logs myapp        # View logs
pm2 logs myapp --lines 100  # Last 100 lines of logs
pm2 restart myapp     # Restart
pm2 reload myapp      # Zero-downtime reload
pm2 stop myapp        # Stop
pm2 delete myapp      # Delete
pm2 monit             # Real-time monitoring

PM2 Auto-start on System Boot

hljs bash
# Generate startup script
pm2 startup

# Run the output command (example):
sudo env PATH=$PATH:/home/user/.nvm/versions/node/v20.11.0/bin pm2 startup systemd -u user --hp /home/user

# Save current PM2 list
pm2 save

Nginx Reverse Proxy Configuration

Nginx runs in front of your Node.js application as a reverse proxy, providing SSL termination, static file serving, and load balancing.

hljs bash
# Install Nginx
sudo apt update
sudo apt install nginx -y

# Create configuration file
sudo nano /etc/nginx/sites-available/myapp
hljs nginx
server {
    listen 80;
    server_name example.com www.example.com;

    # Static files (optional)
    location /static/ {
        alias /var/www/myapp/public/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 86400;
    }
}
hljs bash
# Enable the site
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

SSL Certificate (Let's Encrypt)

hljs bash
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y

# Obtain SSL certificate
sudo certbot --nginx -d example.com -d www.example.com

# Test automatic renewal
sudo certbot renew --dry-run

Deployment Workflow

A simple workflow for deploying updates:

hljs bash
# Pull new code
cd /var/www/myapp
git pull origin main

# Update dependencies
npm install --production

# Database migration (if applicable)
npm run migrate

# Zero-downtime restart with PM2
pm2 reload myapp

# Check logs
pm2 logs myapp --lines 50

On REXE servers, you can close port 3000 via Path Panel. Since Nginx routes traffic from ports 80/443, external access to your Node.js application port is not needed.

Firewall Settings

hljs bash
# Open required ports with ufw
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS
sudo ufw enable

Conclusion

Your Node.js application is now running in a production environment managed by PM2, behind an Nginx reverse proxy, and secured with SSL. With PM2's cluster mode you can fully utilize CPU cores, and with pm2 reload you can perform zero-downtime updates.