Back to Blog
Deploying Node.js Apps on a Linux Server with Nginx, PM2, and Certbot within minutes
linuxbashdevopsdeploymentnodejsnginxpm2

Deploying Node.js Apps on a Linux Server with Nginx, PM2, and Certbot within minutes

A step-by-step guide to deploying a Node.js application on a Linux server, including server setup, Nginx reverse proxy configuration, SSL with Certbot, and process management using PM2.

One of the most important parts of building projects is being able to showcase them and make them accessible for the world to use. Deployment on virtual machines can be a challenge for beginners and sometimes even experienced developers.

In this article, we will go through a step-by-step guide on how to deploy projects on a virtual machine. We will focus on Node.js-based projects, but apart from a few steps, most of the process remains same for deploying projects regardless of stack.

Prerequisites

You will need a Linux server (Ubuntu), you can use any cloud provider like DigitalOcean, AWS, Hetzner, etc. You will also need a domain name pointed to your server's IP address and SSH access to your server.

Step 1: Log into Your Server

SSH into your server using your private key:

ssh -i ./your-key root@your-server-ip

Replace your-key with the path to your SSH key and your-server-ip with your server's actual IP address.

Once you're in, update the system packages:

sudo apt update && sudo apt upgrade -y

Step 2: Install Required Packages

Install Nginx

Nginx will act as a reverse proxy, it sits in front of your Node.js app and forwards requests to it.

sudo apt install nginx

Install Certbot (for SSL)

Certbot automates the process of obtaining and renewing SSL certificates from Let's Encrypt. We install it via snap to get the latest version.

sudo apt install snapd
sudo snap install --classic certbot

You can also refer to the official Certbot instructions for Nginx at certbot.eff.org.

Install Unzip

You might need unzip for extracting archives on the server:

sudo apt install unzip

Install Bun (or Node.js)

If you're using Bun as your runtime:

curl -fsSL https://bun.sh/install | bash

If you prefer Node.js, you can install it via nvm or directly from the NodeSource repository.

Install PM2

PM2 is a process manager that keeps your app running in the background, restarts it on crashes, and manages logs for you.

npm install -g pm2

Step 3: Clone and Build Your Project

Clone your project repository on the server:

git clone https://github.com/your-username/your-repo.git
cd your-repo

Install dependencies:

npm install

Now build the project. If your server has limited memory (which is common on smaller VMs), you can increase the Node.js heap size to prevent out-of-memory crashes:

NODE_OPTIONS="--max-old-space-size=1024" npm run build

This is especially useful on servers with 1GB RAM or less.

Step 4: Configure Nginx as a Reverse Proxy

Nginx will handle all the incoming HTTP/HTTPS traffic and forward it to your Node.js process running on a local port.

Check Nginx Status

First, make sure Nginx is running:

sudo systemctl status nginx

Edit the Nginx Configuration

Open the Nginx config file:

sudo nano /etc/nginx/nginx.conf

Or if you prefer Vim:

vim /etc/nginx/nginx.conf

Add a Server Block

Inside the http block of your nginx.conf (or as a separate file in /etc/nginx/sites-available/), add a server block for your project:

server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}

Replace yourdomain.com with your actual domain and 3000 with the port your app runs on.

If you're deploying multiple projects on the same server, make sure each project uses a different port and has its own server block with a unique server_name. Ports should not overlap between projects.

Test and Restart Nginx

Always test the configuration before restarting:

sudo nginx -t

If the test passes, restart Nginx:

sudo systemctl restart nginx

Step 5: Set Up SSL with Certbot

Now that Nginx is routing traffic to your app, we can secure it with HTTPS.

Get and install certificates (Recommended)

This is the easiest way. Certbot will detect your server blocks, ask which domains you want to secure, and handle the entire SSL configuration for you. It also sets up automatic renewal.

sudo certbot --nginx

Or, just get a certificate

If you want to handle the Nginx SSL configuration yourself:

sudo certbot certonly --nginx

After obtaining the certificate, restart Nginx:

sudo systemctl restart nginx

Step 6: Start Your App with PM2

If you run your app with npm run start directly, it stops when you close the terminal. PM2 solves this by keeping it running as a background process.

Start the app:

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

Replace "my-app" with a name that helps you identify the project, and start with whatever script you need (dev, start, etc.).

If you're using Bun:

pm2 start bun --name "my-app" -- run start

Useful PM2 Commands

# List all running processes
pm2 list

# View logs
pm2 logs my-app

# Restart an app
pm2 restart my-app

# Stop an app
pm2 stop my-app

# Delete an app from PM2
pm2 delete my-app

# Save the current process list (survives server reboot)
pm2 save

# Set PM2 to start on boot
pm2 startup

After starting your app and confirming it works, run pm2 save and pm2 startup so your app automatically restarts if the server reboots.

Quick Reference

Here's the entire flow condensed for when you're deploying your next project:

# SSH into server
ssh -i ./your-key root@your-server-ip

# Clone and build
git clone https://github.com/you/your-repo.git
cd your-repo
npm install
NODE_OPTIONS="--max-old-space-size=1024" npm run build

# Add Nginx server block for your domain
sudo nano /etc/nginx/nginx.conf

# Test and restart Nginx
sudo nginx -t
sudo systemctl restart nginx

# SSL
sudo certbot --nginx

# Start with PM2
pm2 start npm --name "my-app" -- run start
pm2 save

That's it. Your app is now live, secured with SSL, and managed by PM2. The whole process takes a few minutes once you've done it a couple of times.

© 2026. All rights reserved.