Deploying a JavaScript Application on VPS: Advanced Step-by-Step Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: From Local Server to Production-Ready Solution

You have built a powerful JavaScript application on Node.js, tested it locally, and now it's time to show it to the world. Simply uploading files to a VPS and running node app.js is the beginner's path. At an advanced level, deployment includes configuring a reverse proxy server, a process manager, automatic SSL certificate renewal, a continuous integration and delivery (CI/CD) system, as well as proper monitoring and logging.

In this article, we will walk through the step-by-step process of deploying a Node.js application on a VPS (Ubuntu 22.04) at an advanced level. We will configure Nginx as a reverse proxy, PM2 for process management, Let's Encrypt for HTTPS, GitHub Actions for automatic deployment on push to the repository, and add basic monitoring.



Prerequisites

  • A VPS server (Ubuntu 22.04) with SSH access.
  • A domain pointed to your server's IP (e.g., myapp.example.com).
  • Node.js (v18+) and npm installed on the server.
  • Git installed on the server.
  • Your application repository on GitHub.


1. Server Setup and PM2 Process Manager

Let's start with basic environment preparation. After connecting to the server via SSH, update the packages and install the necessary utilities:

sudo apt update && sudo apt upgrade -ysudo apt install nginx git curl -y

Make sure Node.js is installed. If not, install it via NodeSource:

curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -sudo apt install -y nodejsnode -vnpm -v

Now install PM2 globally — an advanced process manager for Node.js that automatically restarts the application on crash, manages logs, and handles clustering:

sudo npm install -g pm2

Clone your application repository and install dependencies:

git clone https://github.com/your-username/your-repository.git /var/www/myappcd /var/www/myappnpm install --production

Create a PM2 ecosystem file (ecosystem.config.js) for fine-tuning:

// ecosystem.config.jsmodule.exports = {  apps: [{    name: 'myapp',    script: 'app.js', // or server.js    instances: 'max', // use all CPU cores    exec_mode: 'cluster',    env: {      NODE_ENV: 'production',      PORT: 3000    },    error_file: '/var/log/node-app/err.log',    out_file: '/var/log/node-app/out.log',    log_date_format: 'YYYY-MM-DD HH:mm Z'  }]};

Create a directory for logs and start the application via PM2:

sudo mkdir -p /var/log/node-appsudo chown -R $USER:$USER /var/log/node-apppm2 start ecosystem.config.jspm2 savepm2 startup systemd

The pm2 startup command will create a systemd service that starts PM2 on system boot. Now your application is running on port 3000, but direct access to it is not secure. Let's move on to configuring Nginx.



2. Configuring Nginx as a Reverse Proxy and SSL via Let's Encrypt

Nginx will accept incoming requests on ports 80 (HTTP) and 443 (HTTPS), proxy them to your Node.js process running on port 3000, and also serve static files directly — this reduces the load on Node.js.

Create a configuration file for our domain:

sudo nano /etc/nginx/sites-available/myapp

Insert the following configuration (replace myapp.example.com with your domain):

server {    listen 80;    server_name myapp.example.com;    return 301 https://$server_name$request_uri; # redirect to HTTPS}

server { listen 443 ssl http2; server_name myapp.example.com;

# SSL ser

Blogs

Book Recommendations