How I Deployed My Node.js App on a VPS With a Custom Domain & Free SSL

I deployed my personal project PBlog to a real VPS, connected it to my custom domain pblog.online, and secured it with free HTTPS. This post documents the exact steps I followed, from a blank server to a fully production-ready app.

If you're new to VPS deployments, this guide will walk you through everything I learned along the way.

1. Creating VPS

I used Kamatera to create an Ubuntu server. Once the server was ready, I connected via SSH:

ssh root@YOUR_SERVER_IP

Image description

2. Installing Node.js and Git

sudo apt update
sudo apt install nodejs git -y

3. Cloning and running the app on a VPS

I cloned my project from GitHub and ran it on that server

git clone https://github.com/pmadhav82/P_blog.git
cd P_blog
npm install

4. Running the app with pm2

To keep the app running 24/7, I installed pm2:

sudo npm install -g pm2

started the app:

pm2 start app.js

Now the app is running on port 8000 and can be accessed by visiting publicIP:8000

5. Installing and Configuring Nginx

I installed Nginx:

sudo apt install nginx -y
Usefull Nginx Commands

Purpose Command
Check Nginx status systemctl status nginx
Start Nginx sudo systemctl start nginx
Stop Nginx sudo systemctl stop nginx
Restart Nginx sudo systemctl restart nginx
Reload Nginx (safe reload after config changes) sudo systemctl reload nginx
Test Nginx configuration sudo nginx -t
View error logs sudo tail -f /var/log/nginx/error.log
List enabled sites ls /etc/nginx/sites-enabled/
List available sites ls /etc/nginx/sites-available/
Enable a site (create symlink) sudo ln -s /etc/nginx/sites-available/yourfile.conf /etc/nginx/sites-enabled/
Disable a site (remove symlink) sudo rm /etc/nginx/sites-enabled/yourfile.conf

At this point, visiting the server's IP address showed the default Nginx welcome page. In order to see the app when visiting the server IP, we have to remove the default Nginx site and create a reverse proxy config.

Creating Reverse Proxy Config

I created a new config file:

sudo nano /etc/nginx/sites-available/pblog.conf

Added this:

server {
    listen 80;
    server_name _;

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

Enabled it:

sudo ln -s /etc/nginx/sites-available/pblog.conf /etc/nginx/sites-enabled/

Tested and reloaded Nginx:

sudo nginx -t
sudo systemctl reload nginx

At this moment, visiting the server IP will show the app.

6. Connecting Domain

I bought a domain from Namecheap. Inside Namecheap -> Advanced DNS, I added two A records where the value is my server's public IP: Image description

I updated the pblog.conf file.

sudo nano /etc/nginx/sites-available/pblog.conf

Updated it to:

server {
    listen 80;
    server_name pblog.online www.pblog.online;

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

and reloaded Nginx; sudo systemctl reload nginx

After a few minutes, pblog.online pointed to my app, but it wasn't secured.

7. Adding Free SSL With Certbot

I installed Certbot:

sudo apt install certbot python3-certbot-nginx -y

Requested SSL:

sudo certbot --nginx -d pblog.online -d www.pblog.online

I followed the prompt and Certbot automatically:

  • Generated certificates

  • Updated my Nginx config

  • Reloaded Nginx

Now my site is live at:

https://pblog.online

Image description

Image description

Deploying my app to a VPS taught me a lot about:

  • Linux servers

  • Nginx reverse proxies

  • DNS configuration

  • SSL certificates

  • Production Node.js setups

Now my project is fully live, secure, and running on my own infrastructure.

If you're deploying your first app, I hope this guide helps you avoid the confusion I had at the beginning.

Comments

No comments