Runtime
    Production

    Deploy Node.js Applications on a VPS

    Production-ready Node.js deployment with PM2 process management, Nginx reverse proxy, SSL encryption, cluster mode, and git-based deployment workflows.

    At a Glance

    RuntimeNode.js 22 LTS
    Process ManagerPM2
    Recommended PlanRamNode Cloud VPS 1 GB+ (2 GB for production)
    OSUbuntu 22.04 or 24.04 LTS
    StackNode.js, PM2, Nginx, Let's Encrypt
    Estimated Setup Time20–30 minutes

    Prerequisites

    • A RamNode VPS running Ubuntu 22.04 or 24.04 (1 GB RAM minimum; 2 GB recommended)
    • Root or sudo access via SSH
    • A registered domain name (optional, but required for SSL)
    1

    Initial Server Setup

    Create a non-root user
    adduser deploy
    usermod -aG sudo deploy
    rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
    Update and configure firewall
    sudo apt update && sudo apt upgrade -y
    sudo ufw allow OpenSSH
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    2

    Install Node.js

    Option A: NodeSource (Recommended)

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

    Option B: nvm (Multi-Version)

    Install via nvm
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
    source ~/.bashrc
    nvm install 22
    nvm use 22
    Install build tools
    sudo apt install -y build-essential
    3

    Deploy a Sample Application

    Create an Express app
    mkdir -p /home/deploy/apps/hello-api
    cd /home/deploy/apps/hello-api
    npm init -y
    npm install express
    app.js
    const express = require('express');
    const app = express();
    const PORT = process.env.PORT || 3000;
    
    app.use(express.json());
    
    app.get('/', (req, res) => {
      res.json({ message: 'Hello from RamNode!', timestamp: new Date().toISOString() });
    });
    
    app.get('/health', (req, res) => {
      res.json({ status: 'ok', uptime: process.uptime() });
    });
    
    app.listen(PORT, '127.0.0.1', () => {
      console.log(`Server running on port ${PORT}`);
    });

    Note: Binding to 127.0.0.1 prevents direct internet access — Nginx will proxy external traffic.

    4

    Process Management with PM2

    Install and start
    sudo npm install -g pm2
    cd /home/deploy/apps/hello-api
    pm2 start app.js --name hello-api
    Essential PM2 commands
    pm2 list                  # View running processes
    pm2 monit                 # Monitor CPU/memory
    pm2 logs hello-api        # View app logs
    pm2 restart hello-api     # Restart app
    Auto-start on boot
    pm2 startup systemd
    # Copy and run the outputted command with sudo
    pm2 save

    Ecosystem File for Multiple Apps

    ecosystem.config.js
    module.exports = {
      apps: [
        {
          name: 'hello-api',
          script: './hello-api/app.js',
          cwd: '/home/deploy/apps',
          instances: 1,
          env: { NODE_ENV: 'production', PORT: 3000 }
        },
        {
          name: 'task-api',
          script: './task-api/app.js',
          cwd: '/home/deploy/apps',
          instances: 'max',
          exec_mode: 'cluster',
          env: { NODE_ENV: 'production', PORT: 3001 }
        }
      ]
    };
    5

    Install and Configure Nginx

    Install Nginx
    sudo apt install -y nginx
    sudo systemctl start nginx
    sudo systemctl enable nginx
    Reverse proxy config
    server {
        listen 80;
        server_name 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_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;
        }
    }
    Enable site
    sudo ln -s /etc/nginx/sites-available/hello-api /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    6

    SSL with Let's Encrypt

    Install Certbot and get certificate
    sudo apt install -y certbot python3-certbot-nginx
    sudo certbot --nginx -d yourdomain.com
    Verify auto-renewal
    sudo systemctl status certbot.timer
    sudo certbot renew --dry-run
    7

    Nginx Performance Tuning

    Gzip compression (nginx.conf http block)
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_min_length 256;
    Rate limiting (nginx.conf http block)
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    Apply with limit_req zone=api_limit burst=20 nodelay; in your location block.

    8

    Git-Based Deployment Workflow

    Set up bare repo on server
    mkdir -p /home/deploy/repos/hello-api.git
    cd /home/deploy/repos/hello-api.git
    git init --bare
    post-receive hook
    #!/bin/bash
    APP_DIR=/home/deploy/apps/hello-api
    GIT_DIR=/home/deploy/repos/hello-api.git
    
    echo "Deploying hello-api..."
    git --work-tree=$APP_DIR --git-dir=$GIT_DIR checkout -f
    cd $APP_DIR
    npm install --production
    pm2 restart hello-api
    echo "Deployment complete."
    Make executable and deploy
    chmod +x /home/deploy/repos/hello-api.git/hooks/post-receive
    
    # On your local machine:
    git remote add production deploy@your-server-ip:/home/deploy/repos/hello-api.git
    git push production main
    9

    Monitoring and Logging

    PM2 log rotation
    pm2 install pm2-logrotate
    pm2 set pm2-logrotate:max_size 10M
    pm2 set pm2-logrotate:retain 7

    Nginx logs: /var/log/nginx/access.log and /var/log/nginx/error.log

    10

    Security Hardening

    Disable root SSH and password auth
    # /etc/ssh/sshd_config
    PermitRootLogin no
    PasswordAuthentication no
    Install Fail2Ban and auto-updates
    sudo systemctl restart sshd
    sudo apt install -y fail2ban
    sudo systemctl enable fail2ban
    sudo systemctl start fail2ban
    
    sudo apt install -y unattended-upgrades
    sudo dpkg-reconfigure -plow unattended-upgrades

    Tip: Add server_tokens off; to Nginx's http block to hide the version.

    Troubleshooting

    • App not responding after reboot: Run pm2 resurrect
    • 502 Bad Gateway: Check pm2 list and verify port matches Nginx proxy_pass
    • EADDRINUSE: Find the conflicting process with sudo lsof -i :3000
    • Certbot fails: Ensure DNS A record points to VPS IP and ports 80/443 are open