PingRite

How to Deploy a Node.js App on a VPS

Andrew D.·April 1, 2026·4 min read · #1004
How to Deploy a Node.js App on a VPS

Deploying a Node.js application on a VPS gives you full control over your environment. This guide walks you through setting up Node.js, configuring PM2, and putting Nginx in front of your app — step by step.

Running a Node.js app on your local machine is one thing — getting it live on a server and keeping it running reliably is another. A VPS (Virtual Private Server) gives you a real Linux environment with root access, so you control every part of the stack. This guide covers how to deploy a Node.js app on a VPS from scratch: installing Node, managing your process with PM2, putting Nginx in front as a reverse proxy, and securing everything with free HTTPS.

What You'll Need Before You Start

This guide assumes you have:

  • A VPS running Ubuntu 22.04 or 24.04 LTS

  • SSH access to the server (root or a sudo user)

  • A domain name pointed at your VPS IP address

  • Your Node.js project code ready to deploy (on GitHub or transferred via SCP/SFTP)

If you're not sure which VPS size to pick, a 2 vCPU / 2 GB RAM instance is a comfortable starting point for most Node.js applications.

image of a human arm taking out a server rack

Step 1: Install Node.js

Ubuntu's default package repositories often ship a significantly outdated version of Node.js. Install from the official NodeSource repository instead to get a current LTS release:

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

Verify the installation:

node -v
 npm -v

If your project needs a specific Node version, consider nvm (Node Version Manager) instead — it lets you switch between versions and pin one per project using a .nvmrc file.

Step 2: Upload Your App and Install Dependencies

Get your code onto the server. If it's on GitHub, clone it directly:

git clone https://github.com/your-username/your-app.git
 cd your-app
 npm install --omit=dev

The --omit=dev flag skips development dependencies. If your app has a build step, run it now:

npm run build

Environment variables

Never hardcode secrets in source files. Create a .env file on the server (not committed to Git) with the values your app needs:

PORT=3000
 NODE_ENV=production
 DATABASE_URL=your-connection-string

Your app should read these via process.env. The dotenv library handles this automatically on startup if you call require('dotenv').config() at the top of your entry file.

Step 3: Keep Your App Running with PM2

Node.js apps exit when they crash or when your SSH session closes. PM2 is a process manager that keeps your app alive, restarts it on failure, and survives server reboots.

Install PM2 globally:

sudo npm install -g pm2

Start your app:

pm2 start app.js --name "my-app"

Or if you use an npm start script:

pm2 start npm --name "my-app" -- start

Configure PM2 to start automatically after a reboot:

pm2 startup
 pm2 save

The pm2 startup command prints a line you need to run as root — copy and paste it exactly. After that, pm2 save freezes the current process list so it's restored on every boot.

Useful PM2 commands

  • pm2 status — see all running processes and their uptime

  • pm2 logs my-app — tail application logs in real time

  • pm2 reload my-app — zero-downtime restart (replaces workers one by one)

  • pm2 restart my-app — full restart with a brief gap in availability

Step 4: Set Up Nginx as a Reverse Proxy

Your Node app listens on an internal port like 3000. Don't expose that port directly to the internet — put Nginx in front to handle public HTTP/HTTPS traffic and forward it to Node.

Install Nginx:

sudo apt install -y nginx

Create a site configuration file:

sudo nano /etc/nginx/sites-available/my-app

Add the following, replacing yourdomain.com with your actual domain:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.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;
    }
}

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
 sudo nginx -t
 sudo systemctl reload nginx

Always run nginx -t first — it validates your config and reports syntax errors before anything is reloaded.

Step 5: Add HTTPS with Let's Encrypt

Free SSL certificates from Let's Encrypt, handled automatically by Certbot:

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

Certbot updates your Nginx config to handle HTTPS and redirect HTTP traffic automatically. Certificates renew every 90 days via a system timer — no manual action needed.

Pushing Updates

Once running, deploying changes is a short sequence of commands:

  1. SSH into the server

  2. cd your-app && git pull

  3. npm install --omit=dev (if dependencies changed)

  4. npm run build (if there's a build step)

  5. pm2 reload my-app — reloads without dropping active connections

For teams or frequent deploys, this is easy to automate with a shell script or a GitHub Actions workflow that SSHs into the VPS and runs these steps on every push to main.

Deploying a Node.js app on a VPS comes down to a few well-understood pieces: Node runs the application, PM2 keeps it alive, and Nginx handles public traffic and SSL termination. Once this stack is in place, it's reliable, low-maintenance, and gives you complete control over your hosting environment.