Host a MERN Stack App on a VPS

By Binamra Lamsal - 8 months ago

Web Development
Host a MERN Stack App on a VPS

Make sure these steps before proceeding

  • Before pushing to github, do not forget to use environment variables where it’s needed. Like for frontend, you might need to setup environment variable for API_URL. Like for backend, you might need to setup environment variable for database url.

  • Make sure to include .gitignore with node_modules inside it so that it gets ignored by github.

  • In backend, make sure that you have start script which starts the backend dev server using node ____.js file. You can use dev script for development server using nodemon. Please don’t use nodemon in production.

<iframe width="560" height="315" src="https://www.youtube.com/embed/rm8AhGGYEVA?si=x8K33p9bgexXUK2X" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

Buy Hostinger VPS Hosting

Click here to Buy India Best Hosting 👉 http://www.hostinger.com/thapa7

For Thapa Family 💥 Use the discount code THAPA7 to get Extra 10% OFF!

After Successful Payment, We will redirect to Homepage, we need to simply click on Start.

Now, then we need to select location

Initial Setup

We need to choose Operating system, We will choose OS with control Panel and click on select

We need to choose the OS for our VPS and we will select Ubuntu 22.04 64bit

Now, we need to set our Panel Password, By default the panel username is admin

Now we need to set the password and ssh key for our VPS Server, Remember you can connect to your VPS using SSH. Open a terminal on your local machine and use the following command, replacing <your-username> and <your-vps-ip> with your actual VPS details:

We just need to review our Information and that’s it.

Our VPS is getting ready to process

We will go with VPS Dashboard

This is it, we are in our VPS Dashboard finally

Go to Home, which is on Navbar, just verify your email for future updates and reference.

But for me, I need to restore to show all things frm start

Also, changed the ssh password too and then try to login

Since we changed the password and I have already added my ip address as trusted host. I need to remove old list.

Finally now, we can login

Steps to Host MERN App

Access Your VPS:

  • Log in to your Hostinger account and access your VPS dashboard.

Connect to Your VPS:

You can connect to your VPS using SSH. Open a terminal on your local machine and use the following command, replacing <your-username> and <your-vps-ip> with your actual VPS details:

ssh <your-username>@<your-vps-ip>

Update and Upgrade:

Before installing any software, it's a good practice to update your VPS packages:

sudo apt update
sudo apt upgrade

sudo:

  • sudo stands for "Superuser Do" or "Substitute User and Do." It is a command that allows users with the appropriate privileges to execute commands as the superuser (administrator) or another user, based on their configuration. This is often used for performing administrative tasks that require elevated permissions.

apt:

  • apt stands for "Advanced Package Tool." It is a command-line package management tool used in Debian-based Linux distributions, including Ubuntu. apt allows you to interact with the system's package manager to install, update, remove, or manage software packages.

So, when you run sudo apt update, you are telling the system to refresh the package repository information, and when you run sudo apt upgrade, you are telling the system to upgrade your installed packages to their latest versions.

Install Node.js using NVM

We are going to install using NVM (Node Version Manager)

Go to NVM’s repo.

The name "curl" stands for "Client for URLs.”curl is a command-line tool and library for transferring data with URLs. curl allows you to send HTTP requests to web servers. You can use it to retrieve web pages, download files, send POST requests, and more.

This command downloads and runs the NVM installation script. It will add NVM to your shell's configuration files (e.g., .bashrc or .zshrc). We need to reload our shell configuration to use NVM since we installed right now.

If you're using Bash, you can try running:

// Since our VPS uses bash shell
source ~/.bashrc

// If your VPS uses zsh shell
source ~/.zshrc

After running, run nvm --version if you're not getting the expected output of the NVM version, it's possible that the nvm command is not recognized in your current shell session. This can happen if the shell session hasn't properly loaded the NVM script. Reconnect to your VPS might solve this issue.

If it's still not working, you can manually run this command:

Once NVM is installed, you can install the desired version of Node.js. For example, to install the latest LTS (Long Term Support) version of Node.js, you can run:

nvm install <node-version>
// Example:
// nvim install 18
// v16 is dead, so please do not use it.

// or
nvm install --lts
// It is recommended to use LTS version for deployment.

Now just to check node.js install or not run the node -v

node -v
// Output: v18.18.0

Install MongoDB:

Choose “Install on Linux” then choose your Linux distribution that your VPS Is using. We are using Ubuntu VPS.

There you will see some steps in “Install MongoDB Community Edition” section, follow that. For Ubuntu, there are 4 steps.

Packages mentioned in Step 1 is installed by default in our VPS, so we can skip that.

The name "curl" stands for "Client for URLs.”curl is a command-line tool and library for transferring data with URLs.

This command installs MongoDB and its components.

Whenever this type of prompt open press Enter. Also the last 4th number command to install mongodb-org takes times. So keep patience.

Extra Tips: If you are wondering Jammy ("Jammy" is a codename for a version of the Ubuntu operating system.) "Jammy" corresponds to Ubuntu 22.04, which is expected to be released in April 2022. Each Ubuntu release also has a version number that includes the year and month of the expected release, so "Ubuntu 22.04" means it's scheduled for release in April 2022”.

Start and Enable MongoDB:

Once MongoDB is installed, start and enable the MongoDB service:

sudo systemctl start mongod
// If the above command is not working:
sudo systemctl enable mongod

Check the Status:

You can check the status of the MongoDB service to ensure it's running without issues:

sudo systemctl status mongod

for the first time, it will show inactive because we haven’t start our MongoDB.

Check Your Ubuntu Version: lsb_release -a

Install Git:

  • You might need Git to clone your MERN application repository. Install it using:

sudo apt install git
  • After installing git, if your github repository is private then you might want to install GitHub CLI too. We need for Ubuntu. So…

After GitHub CLI is installed, use this command follow the steps mentioned by it to automatically setup everything required for authentication:

gh auth login

Clone Your Repository (MERN Application):

Use Git to clone your MERN application's repository into a directory on your VPS.

git clone <github-repo>

cd <your-project-folder>

Setup backend

Now, let’s start with backend. Go inside your backend or server folder and install all dependencies

npm install
yarn install
pnpm install
bun install

// But we are using pnpm in our project, so first we need to instal pnpm and then install dependencies
npm i -g pnpm
pnpm i
ls
// Output:
'  client  package.json  package-lock.json  pnpm-lock.yaml  server

cd server

Setup environment variables

nano .env // Nano is a shell based text editor which comes by default on linux

// We have an environment variable named DB_URL so let's include it.
MONGODB_URI=mongodb://127.0.0.1/vps-test-db
JWT_SECRET=oHSLF&#WsA#WpJ!QCWLz7rCL5^sHgD62Qe8tgb3&SSscRu$sFi*L9MSSgnBM2*!$pyXS&XwHyK8RFFh2cboJv6vy5h#cnpBrymtF*@37!C@ohdHR48@%%4X!Az@wiyCe
PORT=8000

// Then press Ctrl + O and enter to save it.
// The press Ctrl + X to exit out of nano.

// Let's use "cat" command to see if everything is fine.
// The cat command in Linux (short for "concatenate") is primarily used to display the contents of one or more files in the terminal.
cat .env
// This shows content of .env file

Start backend server

npm run start
yarn start
pnpm start // we will use pnpm bcz we use pnpm to instal all the packages
bun run start

The problem with this approach is that if an error occurs then it doesn’t restart itself and your site will be down.

To fix this issue, we can use pm2.

pm2 (Process Manager 2) is a popular process manager for Node.js applications. It is used to manage and monitor Node.js applications, ensuring they run reliably in production environments.

pm2 simplifies the process of managing Node.js applications by allowing you to start, stop, restart, and monitor multiple Node.js processes with a single command.

npm i -g pm2
pm2 start npm --name "test_server" -- start
pm2 ls
pm2 logs test_server

// -- start (there is a space, Keep in mind)
// To test our backend
curl http://localhost:8000
// Note: In our backend we have a route for homepage so we are using it

Here's what each part of the command does:

  • pm2 start npm: This tells pm2 to use npm as the interpreter.

  • -name "test_server": This assigns the name "test_server" to the application.

  • -- start: This specifies that you want to run the "start" script defined in your package.json file.

pm2 -v
// 5.3.0

Here we can clearly see our backend is now live and status is online.

Frontend Setup

Go to frontend or client folder. Install all the dependencies and build the project:

pnpm i
pnpm build
npm run build
bun run build

// setup environment var
nano .env
// We already have some content in our repo so Edit the vite api url to 
http://localhost:8000 
// Note: To make it easier, please setup your project so that it uses API URL environment variable

// as that's our server
// Ctrl + O and Enter to save
// Ctrl + X to exit

Run this command to preview our project:

pnpm preview --host

Now, Here we can’t see our website page, because of firewall. To make our PORT publicly accessible, we need to follow some steps.

Use this command to see status of publicly accessible ports:

sudo ufw status
// The command sudo ufw status is used to check the status of the Uncomplicated Firewall (UFW) on a Linux system. UFW is a user-friendly front-end for managing iptables, which is a powerful firewall management tool in Linux. UFW simplifies the process of configuring and managing firewall rules.

Use this command to allow your port:

sudo ufw allow 4173

Run the command again pnpm preview --host to check is it publicly available or not.

pnpm preview --host

Here, you can see the URL. You can open it on your browser. If you can't see it. You can go to:

http://<your-vps-ip>:<your-frontend-port>   // this is the syntax of the url that we get after running from vps 

// Example
http://62.72.59.218:4173

This works but vite preview is not made for production. It’s recommended to use something like nginx to serve the assets.

sudo ufw deny 4173 // We are just denying the port that we made public earlier.

Nginx is a popular web server and reverse proxy server that is commonly used in VPS (Virtual Private Server) and server environments

You can install nginx using this command:

sudo apt install nginx
// It is installed by default on hostinger vps
ls

cd /etc/nginx

cd sites-enabled/
ls
//default.conf

cat default.conf
// we dont need default.conf file 
rm default.conf

cd /etc/nginx/sites-available
nano 62.72.59.218.conf
// It is common convention to name the file with domain name 
// or ip address based on what you are using
server {
    listen 80;
    root <project-folder>;

    location / {
        try_files $uri $uri/ =404;
    }
}
  • <project-folder>

    • Go to your frontend project where it is, make sure your frontend project is built using npm build. Since, we are using vite, it's inside "dist". It should be inside "build" for create-react-app

    • Our folder: /root/TypeScript-Simple-Mern-Project/client/dist

  • We are using that location context to allow all URLS to render index.html because we are using client side react using react-router-dom for routing.

This configuration sets up an Nginx server to listen on port 80 (HTTP) and serve static files from a specific director.

  1. listen 80;: This specifies that the server should listen on port 80, which is the default port for HTTP traffic.

  2. root /root/TypeScript-Simple-Mern-Project/client/dist;: This sets the document root for this server block. It tells Nginx to serve files from the /root/TypeScript-Simple-Mern-Project/client/dist directory when handling requests to this server block. This is typically used for serving static files like HTML, CSS, JavaScript, and images.

  3. location / { ... }: This is a location block that defines how Nginx should handle requests to the root directory ("/"). It contains the following directives:

    • try_files $uri $uri/ =404;: This directive tells Nginx how to handle requests. It tries to find a matching file in the specified directory ($uri) and, if not found, it appends a trailing slash ($uri/) and, if still not found, it returns a 404 error (=404). Essentially, it serves existing files as-is and sends 404 errors for everything else.

Use this command to check if nginx is working properly:

sudo nginx -t
// nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
// nginx: configuration file /etc/nginx/nginx.conf test is successful
// go to /etc/nginx/sites-enabled
ln -s ../sites-available/62.72.59.218.conf .
// It creates symbolic link (also known as a symlink or soft link) of our configuration file in current dir.

systemctl restart nginx
// to restart the Nginx web server on a Linux system.
// By executing systemctl restart nginx, you ensure that any changes you've made to Nginx's configuration or website files take effect, as well as refresh the server's status.

Now if you go to http://<your-ip-address> then you will see your website. It works right? But let’s see how you can connect it with a proper domain.

Go to your domain DNS settings and If the domain is not from hostinger then change the nameservers to this two.

ns1.dns-parking.com

ns2.dns-parking.com

and Add a new A record:

  • Name: @

  • Points to: your VPS IP address

  • TTL: default

  • Name: www

  • Points to: your domain name (e.g., domain.tld)

  • TTL: default

If you are going to use subdomain then use <subdomain-name> and “www.<subdomain-name>” as name.

// now rename our config to match our domain. 
// go to /etc/nginx/sites-available
// cp 62.72.59.218.conf mywebsite.com.conf
cp 62.72.59.218.conf thapatechnical.online.conf

// now let's edit this configuration to reflect our changes.
// nano mywebsite.com.conf
nano thapatechnical.online.conf

We are copying our previous configuration named "62.72.59.218.conf" and giving it a new name `thapatechnical.online.conf`

server {
        listen 80;
        server_name <domain> www.<domain>;
        root <project-root>;

        location / {
                try_files $uri $uri/ /index.html;
        }
}
  • <domain>

    • Enter your website domain here.

    • For us, it's: server_name thapatechnical.online www.thapatechnical.online

  • <project-root>

    • Enter where your frontend built project is.

    • For us, it's: /root/TypeScript-Simple-Mern-Project/client/dist

// go to /etc/nginx/sites-enabled
ln -s ../sites-available/mywebsite.com.conf 
ln -s ../sites-available/thapatechnical.online.conf
// It creates symlink of our configuration file in current dir.
systemctl restart nginx

Now if you go to mywebsite.com then you will see your website.

Backend domain setup

  • Name: @

  • Points to: your VPS IP address

  • TTL: default

  • Name: www

  • Points to: your domain name (e.g., domain.tld)

  • TTL: default

For backend: api.thapatechnical.online. Our frontend is running on thapatechnical.online and we need to run backend on either server that’s why we are going to create the subdomain of it api.thapatechnical.online

server {
        listen 80;
	server_name <api.domain> www.<api-domain>;

        location / {
                proxy_pass http://localhost:<port>;
                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;
        }
}
  • <api-domain>

    • Put your domain for backend API.

    • For us, it's: server_name api.thapatechnical.online www.api.thapatechnical.online

  • <port>

    • Enter the port where your localhost backend is running. Remember when we started backend using pm2

    • For us, it's: 8000

go to /etc/nginx/sites-enabled
// ln -s ../sites-available/mywebsite.com.conf
ln -s ../sites-available/api.thapatechnical.online.conf
// It creates symlink of our configuration file in current dir.
systemctl restart nginx

now, if you go to http://api.mywebsite.com you will see your api.

Congrats, but let’s update our api from client. Go to client

cd
// Go to your frontend location

nano .env

// Update the api url to new url: http://api.mywebsite.com
// Update the api url to new url: http://api.thapatechnical.online
pnpm build

Now, your website is live.

SSL Setup

We are going to use Let’s Encrypt SSL certificate using certbot. To install certbot:

sudo apt-get install certbot python3-certbot-nginx

// To install certificate to your domain you can use this command:
sudo certbot --nginx -d mywebsite.com -d www.mywebsite.com
sudo certbot --nginx -d thapatechnical.online -d www.thapatechnical.online
// you can also use "sudo certbot --nginx -d mywebsite.com" but since we have "www"
// too we are passing another argument with another website.

// When you do it for first time, it will ask for email, enter any email.
// Then it will tell to agree to conditions or whatever, choose "Y"
// At last, it will tell if you want to share your email, choose "n"

// Now, let's do for our api one
sudo certbot --nginx -d api.mywebsite.com -d www.api.mywebsite.com
sudo certbot --nginx -d api.thapatechnical.online -d www.api.thapatechnical.online

// Congrats, SSL is successfully installed on your website

Note: After installing ssl on your api, you would need to change api url from client because we had used “http” at that time. So, go to your client, update your API URL with https and build the project.