Quick Answer: Production MERN Stack Deployment#
Production Deployment Topology#
┌─────────────────────────────────────────────────────────────┐
│ Public Internet │
└─────────────────────────────┬─────────────────────────────┘
│ HTTPS (Port 443) / Let's Encrypt SSL
┌─────────────────────────────▼─────────────────────────────┐
│ Linux VPS (Ubuntu 24.04 LTS) │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Nginx Reverse Proxy & Static CDN │ │
│ │ • Serves compiled React/Next.js frontend assets │ │
│ │ • Proxies /api requests to localhost:5000 │ │
│ │ • Gzip/Brotli Compression & Security Headers │ │
│ └──────────────────────────┬────────────────────────────┘ │
│ │ Reverse Proxy (HTTP/1.1) │
│ ┌──────────────────────────▼────────────────────────────┐ │
│ │ PM2 Cluster (Node.js / Express) │ │
│ │ • Worker 1 (Port 5000) • Worker 2 (Port 5000) │ │
│ │ • Auto-restart on crash & boot startup hook │ │
│ └──────────────────────────┬────────────────────────────┘ │
│ │ TCP Socket (127.0.0.1:27017) │
│ ┌──────────────────────────▼────────────────────────────┐ │
│ │ MongoDB Database (Private Auth Only) │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Step 1: Server Hardening & Firewall (UFW) Configuration#
# 1. Update OS packages
sudo apt update && sudo apt upgrade -y
# 2. Configure Firewall (Only allow SSH, HTTP, and HTTPS)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Step 2: PM2 Process Management & Zero-Downtime Reloads#
PM2 ensures your Node.js backend restarts automatically if an unhandled exception occurs and leverages all available CPU cores:
#1. PM2 Ecosystem File (ecosystem.config.js)
module.exports = {
apps: [
{
name: 'mern-backend-api',
script: './dist/server.js',
instances: 'max', // Scale to all CPU cores
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
PORT: 5000,
},
max_memory_restart: '1G',
listen_timeout: 10000,
kill_timeout: 5000,
},
],
};
#2. Start & Enable Boot Startup
# Start cluster
pm2 start ecosystem.config.js --env production
# Configure systemd startup script so PM2 survives server reboots
pm2 save
pm2 startup
Step 3: Hardened Nginx Reverse Proxy Configuration#
Create /etc/nginx/sites-available/app.conf:
# Rate Limiting Zone: 10 requests/second per IP
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
listen 80;
server_name api.rajputbhavin.engineer;
# Security Headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Gzip Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
location / {
limit_req zone=api_limit burst=20 nodelay;
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;
proxy_read_timeout 60s;
}
}
Enable the configuration and obtain automated SSL:
sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# Install free Let's Encrypt SSL Certificate
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d api.rajputbhavin.engineer
4 Common Deployment Failures to Prevent#
- ✓Exposing MongoDB Port 27017 to the Public Internet: Ensure
bindIp: 127.0.0.1is set in/etc/mongod.conf. Never expose your database port to the open web without VPC peering. - ✓Storing Secrets in
.envFiles in Git: Add.envto.gitignore. Inject secrets via environment variables or secret managers (AWS Secrets Manager, Doppler). - ✓Hardcoding
localhost:5000in React Bundles: Use environment variables (process.env.NEXT_PUBLIC_API_URL) to dynamically target production URLs during compilation. - ✓Ignoring Log Rotation: Node.js log files will eventually fill the entire server disk. Install
pm2-logrotateto compress and rotate logs automatically.
Enterprise DevOps & Deployment with Rajput Bhavin Engineering#
Need help designing bulletproof cloud infrastructure, CI/CD pipelines, or containerized deployments?
- ✦Explore our Custom Enterprise Software Development
- ✦Discover our SaaS Development Services
- ✦Schedule an Infrastructure Consultation with Rajput Bhavin today.