Learn how to deploy your MERN (MongoDB, Express, React, Node.js) application on a VPS server with proper domain setup and SSL certificates.
📋 Before You Start
Make sure you've completed these steps:
- Use environment variables for sensitive data (API URLs, database connections)
- Add
node_modulesto.gitignore - Use
nodefor production (notnodemon) - Push your code to GitHub
🛒 Step 1: Get a VPS
I recommend Hostinger VPS for beginners:
- Visit Hostinger
- Select VPS Hosting
- Choose your location (closer to users = faster)
- Select Ubuntu 22.04 64bit with control panel
- Set admin password and SSH key
- Wait for VPS setup (5-10 minutes)
After setup, verify your email for updates and save your VPS IP address!
🔌 Step 2: Connect to Your VPS
SSH (Secure Shell) allows you to remotely access and control your VPS through the command line. Think of it as remotely controlling another computer.
Open terminal and connect via SSH:
ssh root@<your-vps-ip>
# Example: ssh root@62.72.59.218
# You'll be prompted for the password you set during VPS setupWhat is sudo?
sudo= "Superuser Do" - runs commands with admin privilegesapt= Package manager for Ubuntu (like App Store for Linux)update= Refreshes the list of available packagesupgrade= Installs the latest versions of all packages
Update system packages:
sudo apt update
sudo apt upgrade
# Press 'Y' when prompted to confirm installationThis ensures your VPS has the latest security patches and software versions.
📦 Step 3: Install Node.js (Using NVM)
Why NVM? NVM (Node Version Manager) lets you install and switch between multiple Node.js versions easily. Perfect for managing different projects!
Install NVM (Node Version Manager):
# curl downloads files from the internet
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bashReload shell configuration to activate NVM:
# This reloads your shell settings without logging out
source ~/.bashrc
# Verify NVM is installed
nvm --versionWhat is LTS? Long Term Support - stable, reliable version recommended for production.
Install Node.js LTS:
nvm install --lts
# Verify installation
node -v
# Output: v18.18.0 (or latest LTS version)
npm -v
# Output: 9.8.1 (npm comes with Node.js)If nvm command not found, close terminal and reconnect to your VPS.
🍃 Step 4: Install MongoDB
MongoDB is your database. These commands install the official MongoDB Community Edition on Ubuntu.
Step 4.1: Import MongoDB GPG key (for security verification):
# This verifies that the MongoDB package is authentic
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-7.0.gpgStep 4.2: Add MongoDB repository to Ubuntu's package list:
# "jammy" is the codename for Ubuntu 22.04
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.listStep 4.3: Install MongoDB:
sudo apt update
sudo apt install -y mongodb-org
# This may take 2-3 minutes
# Press Enter if you see any promptsStep 4.4: Start MongoDB service:
# Start MongoDB now
sudo systemctl start mongod
# Enable MongoDB to start automatically on server reboot
sudo systemctl enable mongod
# Check if MongoDB is running
sudo systemctl status mongod
# Look for "active (running)" in green
# Press 'q' to exit status viewIf you see "active (running)", MongoDB is successfully installed and running!
🔧 Step 5: Install Git & Clone Your Project
Git helps you download and manage your code from GitHub.
Step 5.1: Install Git:
sudo apt install git
# Verify installation
git --version
# Output: git version 2.34.1 (or similar)Step 5.2: For private repositories, install GitHub CLI:
# Install GitHub CLI
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gh -y
# Authenticate with GitHub
gh auth login
# Follow the prompts:
# - Choose "GitHub.com"
# - Choose "HTTPS"
# - Choose "Login with a web browser"
# - Copy the code and press Enter
# - Paste code in browser to authenticateStep 5.3: Clone your repository:
# For public repos:
git clone https://github.com/username/repo-name.git
# For private repos (after gh auth login):
git clone https://github.com/username/private-repo.git
# Navigate into your project
cd <your-project-folder>
# Verify you're in the right place
ls
# You should see your project filesReplace username/repo-name with your actual GitHub username and repository name!
⚙️ Step 6: Setup Backend
Step 6.1: Navigate to your backend folder:
# If your backend is in a folder called 'server' or 'backend'
cd server
# or
cd backend
# Check what's inside
ls
# You should see: package.json, index.js, etc.Step 6.2: Install dependencies:
# Using npm (comes with Node.js)
npm install
# OR use pnpm (faster alternative)
npm i -g pnpm
pnpm installStep 6.3: Create environment variables:
# nano is a text editor in terminal
nano .envAdd your environment variables (use arrow keys to navigate):
MONGODB_URI=mongodb://127.0.0.1/your-database-name
JWT_SECRET=your-secret-key-here-make-it-long-and-random
PORT=8000
NODE_ENV=productionNano Editor Controls:
Ctrl + OthenEnterto saveCtrl + Xto exit
Verify your .env file:
# Display file contents
cat .envUse PM2 for Production
Why PM2? PM2 keeps your app running 24/7, restarts it if it crashes, and manages logs.
Step 6.4: Install PM2 globally:
npm i -g pm2
# Verify installation
pm2 -v
# Output: 5.3.0 (or similar)Step 6.5: Start your backend with PM2:
# Make sure you're in your backend folder first!
# The "--" is important - don't forget the space!
pm2 start npm --name "backend" -- start
# View all running apps
pm2 ls
# View logs in real-time (Ctrl+C to exit)
pm2 logs backend
# Save PM2 configuration
pm2 save
# Setup PM2 to start on server reboot
pm2 startup
# Copy and run the command it shows youStep 6.6: Test if backend is working:
# This sends a request to your backend
curl http://localhost:8000
# You should see your API response
# Or test a specific route
curl http://localhost:8000/api/health✅ PM2 automatically restarts your app if it crashes! ✅ Your backend will start automatically when server reboots!
🎨 Step 7: Setup Frontend
Step 7.1: Navigate to your frontend folder:
# If you're in backend folder, go back and enter client folder
cd ../client
# or from project root:
cd client
# Verify you're in the right place
ls
# You should see: package.json, src/, public/, etc.Step 7.2: Install frontend dependencies:
pnpm install
# or
npm install
# This may take 2-5 minutes depending on project sizeStep 7.3: Create environment variables:
nano .envAdd your API URL (adjust based on your framework):
# For Vite (React/Vue)
VITE_API_URL=http://localhost:8000
# For Create React App
REACT_APP_API_URL=http://localhost:8000
# For Next.js
NEXT_PUBLIC_API_URL=http://localhost:8000Press Ctrl + O, Enter to save, then Ctrl + X to exit.
Step 7.4: Build the production version:
pnpm build
# or
npm run build
# This creates optimized files in 'dist' or 'build' folder
# Wait for build to complete (1-3 minutes)Verify build was successful:
# Check if build folder exists
ls dist # for Vite
ls build # for Create React App
# You should see index.html, assets/, etc.The build command creates optimized, minified files ready for production deployment.
🌐 Step 8: Setup Nginx
What is Nginx? Nginx is a web server that serves your frontend files to visitors. Think of it as the waiter that delivers your website to users.
Step 8.1: Install Nginx:
sudo apt install nginx
# Verify installation
nginx -v
# Output: nginx version: nginx/1.18.0
# Check if Nginx is running
sudo systemctl status nginxStep 8.2: Create Nginx configuration file:
# Navigate to Nginx config directory
cd /etc/nginx/sites-available
# Create a config file named with your IP
nano <your-ip>.conf
# Example: nano 62.72.59.218.confStep 8.3: Add this configuration (replace <your-project> with your actual folder path):
server {
listen 80;
# Full path to your built frontend files
root /root/<your-project>/client/dist;
# For Create React App, use: /root/<your-project>/client/build
location / {
# This ensures React Router works properly
try_files $uri $uri/ /index.html;
}
}Example: If your project is at /root/my-mern-app/client/dist:
root /root/my-mern-app/client/dist;Save with Ctrl + O, Enter, then Ctrl + X.
Step 8.4: Enable your configuration:
# Go to enabled sites folder
cd /etc/nginx/sites-enabled
# Remove default configuration
rm default
# or
rm default.conf
# Create symbolic link to your config
ln -s ../sites-available/<your-ip>.conf .
# Verify the link was created
ls -laStep 8.5: Test and restart Nginx:
# Test if configuration is valid
sudo nginx -t
# You should see: "syntax is ok" and "test is successful"
# If test passed, restart Nginx
sudo systemctl restart nginx
# Check Nginx status
sudo systemctl status nginx
# Should show "active (running)" in greenStep 8.6: Open firewall port:
# Allow HTTP traffic (port 80)
sudo ufw allow 80
# Check firewall status
sudo ufw statusStep 8.7: Test your website:
Visit http://<your-vps-ip> in your browser! 🎉
If you see your website, congratulations! Your frontend is now live on the internet!
Not working? Check these:
- Is the path in Nginx config correct? (
ls /root/<your-project>/client/dist) - Did Nginx test pass? (
sudo nginx -t) - Is Nginx running? (
sudo systemctl status nginx) - Is port 80 open? (
sudo ufw status)
🌍 Step 9: Connect Custom Domain
Why use a domain? Instead of using http://62.72.59.218, you can use https://yourdomain.com - much more professional!
Step 9.1: Configure DNS Settings
Go to your domain registrar (GoDaddy, Namecheap, Hostinger, etc.) and find DNS settings.
Add these A records:
| Name | Type | Points To | TTL |
|---|---|---|---|
| @ | A | your-vps-ip | 3600 |
| www | A | your-vps-ip | 3600 |
| api | A | your-vps-ip | 3600 |
What do these mean?
@= Your root domain (yourdomain.com)www= With www prefix (www.yourdomain.com)api= Subdomain for backend (api.yourdomain.com)- TTL = Time To Live (how long DNS info is cached)
DNS Propagation: Wait 5-30 minutes for changes to take effect worldwide. Sometimes it can take up to 24 hours.
Check if DNS is working:
# On your local computer terminal
ping yourdomain.com
# Should show your VPS IP addressStep 9.2: Frontend Domain Config
cd /etc/nginx/sites-available
nano yourdomain.com.confAdd configuration:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
root /root/<your-project>/client/dist;
location / {
try_files $uri $uri/ /index.html;
}
}Backend API Config
nano api.yourdomain.com.confAdd configuration:
server {
listen 80;
server_name api.yourdomain.com www.api.yourdomain.com;
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;
}
}Create symlinks:
cd /etc/nginx/sites-enabled
ln -s ../sites-available/yourdomain.com.conf .
ln -s ../sites-available/api.yourdomain.com.conf .
systemctl restart nginxUpdate frontend API URL:
cd /root/<your-project>/client
nano .env
# Change to: VITE_API_URL=http://api.yourdomain.com
pnpm build🔒 Step 10: Install SSL Certificate (HTTPS)
Install Certbot:
sudo apt-get install certbot python3-certbot-nginxInstall SSL for frontend:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.comInstall SSL for API:
sudo certbot --nginx -d api.yourdomain.com -d www.api.yourdomain.comFirst time? Enter your email, agree to terms, and choose 'N' for sharing email.
Update API URL to HTTPS:
cd /root/<your-project>/client
nano .env
# Change to: VITE_API_URL=https://api.yourdomain.com
pnpm build🎯 Useful PM2 Commands
pm2 list # List all apps
pm2 logs backend # View logs
pm2 restart backend # Restart app
pm2 stop backend # Stop app
pm2 delete backend # Delete app
pm2 save # Save current list
pm2 startup # Auto-start on reboot🐛 Common Issues
MongoDB Not Starting?
sudo systemctl status mongod
sudo systemctl restart mongodNginx Not Working?
sudo nginx -t
sudo systemctl restart nginxFrontend Not Loading?
Check build folder path in Nginx config:
ls /root/<your-project>/client/distAPI Not Connecting?
Check if PM2 is running:
pm2 list
curl http://localhost:8000🎉 Congratulations!
Your MERN stack app is now live with:
- ✅ Custom domain
- ✅ HTTPS/SSL certificate
- ✅ Auto-restart on crash (PM2)
- ✅ Production-ready Nginx setup
Your site is now accessible at https://yourdomain.com and API at https://api.yourdomain.com!
📚 Quick Reference
VPS Setup:
ssh root@<vps-ip>
sudo apt update && sudo apt upgradeNode.js:
nvm install --ltsMongoDB:
sudo systemctl start mongodPM2:
pm2 start npm --name "app" -- startNginx:
sudo nginx -t
systemctl restart nginxSSL:
sudo certbot --nginx -d domain.comNeed help? Drop a comment below! 💬
Found this useful? Share it with other developers! 🚀