Developers wanting full control over their server infrastructure, running Node.js backends and React frontends on affordable Cloud VPS instances.
- ✓A clean Ubuntu 22.04 or 24.04 LTS VPS instance
- ✓A domain name pointing to the VPS IP address
- ✓Basic terminal and SSH knowledge
🚀 What You Will Learn & Build
- ▸Setting up a non-root sudo user and UFW firewall
- ▸Installing Node.js LTS via NodeSource and PM2 process manager
- ▸Building and serving React frontend static files with Nginx
- ▸Reverse-proxying /api requests to Express running on localhost:5000
- ▸Securing the server with a free Let's Encrypt SSL certificate
1. Initial Server Security and Node.js Installation
# 1. Update server packages
sudo apt update && sudo apt upgrade -y
# 2. Install Node.js LTS (v20) via NodeSource
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs git build-essential
# 3. Verify versions
node -v
npm -v
# 4. Install PM2 globally to keep your Express server running 24/7
sudo npm install -g pm22. Running the Express Backend with PM2
# Clone backend
cd /var/www
sudo git clone https://github.com/yourusername/mern-backend.git backend
cd backend
npm install --production
# Create production .env
nano .env
# Start with PM2
pm2 start server.js --name "mern-api"
# Configure PM2 to restart automatically on server reboot
pm2 startup
# Copy and run the command printed by pm2 startup, then:
pm2 save3. Configuring Nginx as Reverse Proxy and Static Server
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# React compiled frontend build files
root /var/www/frontend/dist;
index index.html;
# Handle client-side routing in React (React Router)
location / {
try_files $uri $uri/ /index.html;
}
# Proxy API requests to Node.js backend
location /api/ {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass $http_upgrade;
}
}4. Enabling Free SSL Certificate with Certbot
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com✨ Expected Output & Verification
Visiting https://yourdomain.com loads your React single-page app over HTTPS. API requests to https://yourdomain.com/api/posts resolve seamlessly to Express on port 5000 without any CORS errors.
🧪 How to Test the Implementation
- 1Run `pm2 status` to verify mern-api is online and consuming healthy memory (< 150MB)
- 2Test client-side navigation (e.g. refresh /dashboard directly to verify try_files works)
- 3Check `sudo certbot renew --dry-run` to ensure SSL renewal is configured correctly
⚠️ Common Errors & How to Fix Them
Root Cause: Nginx trying to find actual file matching the URL path instead of falling back to index.html.
Fix: Add 'try_files $uri $uri/ /index.html;' inside location / in your Nginx config.
Root Cause: Express server is crashed or not listening on port 5000.
Fix: Run 'pm2 logs mern-api' to inspect Node.js crash errors and ensure PORT matches proxy_pass.
🛡️ Security Considerations & Best Practices
- !Configure UFW firewall: sudo ufw allow OpenSSH && sudo ufw allow 'Nginx Full' && sudo ufw enable.
- !Never bind MongoDB to 0.0.0.0 without authentication enabled.
❓ Frequently Asked Questions (FAQ)
If server.js encounters an unhandled exception or the VPS reboots, plain 'node' crashes and stays down. PM2 automatically restarts the process instantly.
📌 Summary of Key Takeaways
- ✓Use PM2 for zero-downtime Node.js process management.
- ✓Use Nginx to serve static React builds and reverse-proxy /api traffic.
- ✓Secure with Certbot Let's Encrypt SSL.
Official References & Further Reading
- 🔗Nginx Official Documentation— Official guide on reverse proxying and try_files directives.
Written by Manoj Neupane
AuthorSoftware developer and programming instructor, Nepal
Building software systems and SaaS platforms with Laravel, Next.js, and Node.js. Passionate about helping students and developers in Nepal master practical, real-world development skills.