What You'll Build

This guide deploys a Node.js application (Express, Fastify, Next.js, or any framework using a listening HTTP server) to an Ubuntu 22.04 or 24.04 LTS VPS. The production architecture is simple and robust: Node runs under PM2 for process management, systemd supervises PM2 itself, and Nginx handles TLS, compression, and static assets in front of it.

Install Node.js from NodeSource

The Ubuntu archive ships an older Node release. For current LTS versions (20.x or 22.x), use the NodeSource repository.

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node --version
npm --version

Install build-essential so native addons (sharp, bcrypt, better-sqlite3) compile when needed:

sudo apt install build-essential -y

Create an App User

Never run Node as root. Create a dedicated system user with a home directory under /opt:

sudo adduser --system --group --home /opt/nodeapp nodeapp
sudo mkdir -p /opt/nodeapp && sudo chown nodeapp:nodeapp /opt/nodeapp

Deploy the Application

Switch to the app user and clone your repository. Install dependencies without dev packages and build any assets.

sudo -u nodeapp -H bash
cd /opt/nodeapp
git clone https://github.com/yourorg/yourapp.git app
cd app
npm ci --omit=dev
npm run build    # if applicable

Copy your environment template to a .env file and fill in production values. Keep it readable only by the nodeapp user:

cp .env.example .env
chmod 600 .env

Test the app manually:

NODE_ENV=production node server.js

Once it listens on the configured port (commonly 3000), stop it with Ctrl+C.

Use PM2 for Process Management

PM2 keeps your Node process alive, handles log files, and can run multiple instances in cluster mode to use all CPU cores.

sudo npm install -g pm2
pm2 start server.js --name myapp -i max
pm2 save

The -i max flag spawns one worker per CPU core, giving you parallel request handling without changing application code. pm2 save persists the process list so PM2 can resurrect it on boot.

Generate the systemd integration so PM2 starts with the server:

sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u nodeapp --hp /opt/nodeapp

PM2 prints a command — run it as root to install the service. From now on, systemctl status pm2-nodeapp shows the supervisor's status and PM2 resurrects on reboot.

Essential PM2 Commands

CommandPurpose
pm2 listShow running apps
pm2 logs myappTail stdout and stderr
pm2 restart myappRestart after code changes
pm2 reload myappZero-downtime reload (cluster mode)
pm2 monitLive CPU and memory dashboard
pm2 savePersist process list

Configure Nginx as a Reverse Proxy

Nginx handles TLS, HTTP/2, gzip, and static file caching in front of Node. Install it and create a site config:

sudo apt install nginx -y
sudo nano /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name example.com www.example.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;
    }
}

The Upgrade and Connection headers matter for WebSocket apps (Socket.IO, Next.js hot reload, real-time features). Enable and reload:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

For tuning details — rate limiting, gzip, buffer sizes — see our Nginx reverse proxy guide.

Add HTTPS with Certbot

After DNS propagates, issue a Let's Encrypt certificate:

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

Certbot rewrites the Nginx config to listen on 443 and installs a renewal timer. For premium or wildcard certs, see our SSL certificate offerings.

Handle Deployments Cleanly

A simple deploy script can pull new code, install production dependencies, rebuild assets, and reload PM2 with zero downtime:

#!/bin/bash
set -e
cd /opt/nodeapp/app
git pull origin main
npm ci --omit=dev
npm run build
pm2 reload myapp --update-env

Run it as the nodeapp user via SSH key from your CI runner. For larger teams, consider blue-green deploys behind Nginx upstream groups.

Operational Tips

Running Node.js in production? MassiveGRID's Cloud VPS gives you NVMe storage, root access, and four global data centers for low-latency Node hosting. Explore our developer plans or contact our team to discuss your deployment.

Published by MassiveGRID, your trusted Linux VPS hosting partner. Explore our Cloud VPS plans for root-access Ubuntu hosting.