Seamlessly Deploy a MERN Application on Ubuntu with Nginx, PM2, MongoDB, and SSL

By Rajiv Raut July 19, 2026
cover-image-linux

Introduction

The MERN stack (MongoDB, Express.js, React.js, and Node.js) is one of the most popular technologies for building modern web applications. While developing a MERN application locally is relatively straightforward, deploying it to a production server (VPS) requires additional components such as a web server, process manager, firewall, and SSL certificate.

This guide explains how to deploy a MERN application on an Ubuntu server using:

  • Node.js for running the backend application
  • MongoDB for database management
  • PM2 for process management
  • Nginx as a reverse proxy and web server
  • UFW for firewall protection
  • Let’s Encrypt for SSL certificates

The goal is to create a secure, reliable, and production-ready deployment environment.

Understanding the Deployment Architecture

Before beginning the deployment process, it is important to understand how the different components work together.

Visitor Browser Request flow

React Frontend

React is responsible for rendering the user interface. After creating a production build, React generates static files such as HTML, CSS, and JavaScript.

These files are served directly by Nginx.

Express Backend

Express handles:

  • API requests
  • Authentication
  • Business logic
  • Database operations
  • Data validation

The backend runs as a Node.js application and communicates with MongoDB.

MongoDB

MongoDB stores application data using collections and documents.

Typical examples include:

  • Users
  • Products
  • Orders
  • Blog posts
  • Job applications

Nginx

Nginx acts as a reverse proxy between visitors and the Node.js application.

Benefits include:

  • SSL termination
  • Better performance
  • Static file serving
  • Security improvements
  • Load balancing support

PM2

PM2 is a process manager for Node.js applications.

It ensures that:

  • Applications remain online
  • Crashed processes restart automatically
  • Applications start after server reboots
  • Logs are easily accessible

UFW Firewall

UFW (Uncomplicated Firewall) controls network access to the server.

Only necessary ports should be exposed publicly.

SSL Certificates

SSL encrypts communication between visitors and the server.

Benefits include:

  • Secure authentication
  • Data protection
  • Better SEO rankings
  • Browser trust
  • Compliance with modern security standards

Understanding Request Flow

When a visitor accesses your website:

https://example.com

The request follows this path:

  1. Browser sends request to Nginx.
  2. Nginx serves React frontend files.
  3. React loads inside the browser.
  4. React requests data from backend APIs.
  5. Nginx forwards API requests to Node.js.
  6. Node.js processes the request.
  7. MongoDB stores or retrieves data.
  8. Node.js sends a response.
  9. React updates the user interface.
Request flow of MERN Application

Understanding this workflow helps identify issues during troubleshooting.

Development vs Production

Many developers run MERN applications locally using:

npm run dev

or

npm start

This is suitable for development but not production.

Development Environment

Characteristics:

  • Hot reloading
  • Debugging enabled
  • Local machine usage
  • Temporary processes

Production Environment

Characteristics:

  • Optimized frontend build
  • Reverse proxy
  • SSL encryption
  • Process monitoring
  • Firewall protection
  • Automatic recovery after crashes

This guide focuses on creating a production-ready environment.

Prerequisites

Before starting, ensure you have:

  • Ubuntu server with sudo access
  • Registered domain name
  • DNS management access
  • Git repository containing the MERN application
  • SSH access to the server

Step 1: Update the Server

Update installed packages:

sudo apt update -y
updating the local repo list
sudo apt upgrade -y
Upgrading the packages to the newer versions

Keeping the system updated improves security and stability.

Step 2: Install Node.js

Add the official Node.js repository:

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
Adding Node.js repository to the local repo list

Install Node.js:

sudo apt install nodejs -y
Installing Node.js package

Verify installation:

node -v
npm -v

Both commands should return version numbers.

Verifying Node and NPM versions

Step 3: Install MongoDB

Install required packages:

sudo apt install curl gnupg ca-certificates -y
Ensuring that the system have required packages required for installing mongodb

Import MongoDB’s signing key:

curl -fsSL https://pgp.mongodb.com/server-8.0.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server.gpg
Importing MongoDB's signing key

Add the MongoDB repository:

echo "deb [ signed-by=/usr/share/keyrings/mongodb-server.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" \
| sudo tee /etc/apt/sources.list.d/mongodb-org.list
Adding the MongoDB repository to the local repo list

Update package information:

sudo apt update

Install MongoDB:

sudo apt install mongodb-org -y
Installing MongoDB to the system

Start and enable the service:

sudo systemctl enable --now mongod
Enabling the mongoDB package and setting it up as auto start

Verify status:

systemctl status mongod
Checking the status of MongoDB

Open MongoDB shell:

mongosh
Opening the MongoDB shell

Step 4: Install Nginx

Install Nginx:

sudo apt install nginx -y
Installing the nginx package to the system

Enable and start the service:

sudo systemctl enable --now nginx
Enabling the nginx package and setting it as auto start

Verify status:

systemctl status nginx
Checking the status of nginx

Visiting the server IP address should display the default Nginx page.

Example: http://157.121.54.10

verifying whether nginx is working or not

Step 5: Clone the Application Repository

Clone your application repository:

git clone https://github.com/your-username/your-project.git
cloning the MERN project from the github

Move into the project directory:

cd your-project
Moving into the project directory

Note: Replace the repository URL with your own project repository.

Step 6: Configure the Backend

Navigate to the backend directory:

cd backend

Install dependencies:

npm install
Moving into backend directory and installing the required backend packages

Install PM2:

sudo npm install -g pm2
Installing PM2 package

Start the application:

pm2 start server.js --name backend-app
Starting the backend application

Note: Replace server.js with your application’s entry file.

Save the process list:

pm2 save

Enable startup:

pm2 startup
Saving the pm2 process and setting it as startup

Execute the command displayed by PM2.

Executing the command displayed by pm2

Verify:

pm2 list

The application should show an online status.

Listing the pm2 process

Step 7: Build the Frontend

Navigate to the frontend directory:

cd ../frontend
Navigating to the frontend directory

Install dependencies:

npm install
Installing the required frontend packages

Create a production build:

npm run build
Creating a production build

React generates a build directory containing optimized static files.

Step 8: Configure DNS

Create DNS records pointing the domain to the server.

Example:

Record TypeHostValue
A@Server IP
AwwwServer IP

Wait for DNS propagation before continuing.

Adding A record to the DNS zone
Adding CNAME record to the DNS zone

Step 9: Deploy Frontend Files

Create a web root directory:

sudo mkdir -p /var/www/example.com
Creating a web root directory

Copy build files:

sudo cp -r build/. /var/www/example.com/
copying the frontend build files

Set ownership:

sudo chown -R www-data:www-data /var/www/example.com
Setting the ownership of the web root directory

Set permissions:

sudo chmod -R 755 /var/www/example.com
Setting the permissions for the project files

Step 10: Configure Nginx Reverse Proxy

Create a configuration file:

sudo nano /etc/nginx/sites-available/mern-app

Example configuration:

server {
    listen 80;

    server_name example.com www.example.com;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://127.0.0.1:3000;

        proxy_http_version 1.1;

        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;
    }
}
Configuring the nginx

Enable the site:

sudo ln -s /etc/nginx/sites-available/mern-app \
/etc/nginx/sites-enabled/
Enabling the site by linking the nginx configuration

Disable the default site:

sudo rm -f /etc/nginx/sites-enabled/default
Removing the nginx default site

Validate configuration:

sudo nginx -t
Validating the nginx configuration

Reload Nginx:

sudo systemctl reload nginx
Reloading the nginx service

Step 11: Configure Firewall

Allow SSH:

sudo ufw allow OpenSSH

Allow HTTP:

sudo ufw allow 80/tcp

Allow HTTPS:

sudo ufw allow 443/tcp

Enable firewall:

sudo ufw enable
Configuring the ufw (firewall)

Verify rules:

sudo ufw status
Verifying the firewall rules by checking its status

Step 12: Install SSL Certificate

Install Certbot:

sudo apt install certbot python3-certbot-nginx -y
Installing certbot for nginx

Generate a certificate:

sudo certbot --nginx -d example.com -d www.example.com
Generating SSL certificates for the domains using certbot

Verify renewal:

sudo certbot renew --dry-run
Verifying SSL auto renewal

After successful completion, your site should be accessible through HTTPS.

site became live

Verification Checklist

Before going live, verify the following:

  • MongoDB service is running
  • PM2 application status is online
  • Nginx configuration passes validation
  • React frontend loads correctly
  • API requests reach the backend
  • DNS resolves correctly
  • SSL certificate is active
  • Firewall rules are configured
  • HTTPS works without warnings
Pre-deployment verification checklist

Troubleshooting Guide

502 Bad Gateway

Symptoms

  • Nginx displays a 502 error
  • API requests fail

Solution

Check PM2 status:

pm2 list

View logs:

pm2 logs

Verify backend port:

ss -tulpn

Restart application:

pm2 restart backend-app

React Routes Return 404

Symptoms

Refreshing routes such as:

/dashboard
/profile
/settings

returns a 404 error.

Solution

Verify Nginx contains:

location / {
    try_files $uri $uri/ /index.html;
}

Reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

MongoDB Not Starting

Check service status:

systemctl status mongod

View logs:

sudo journalctl -u mongod

Restart:

sudo systemctl restart mongod

Backend Cannot Connect to MongoDB

Verify MongoDB status:

systemctl status mongod

Test connection:

mongosh

Verify the MongoDB connection string inside environment variables.

SSL Certificate Generation Fails

Verify DNS:

dig example.com

Check:

  • Correct server IP
  • DNS propagation
  • Port 80 accessibility

If using Cloudflare, temporarily switch DNS records to DNS Only mode.

Nginx Configuration Errors

Validate configuration:

sudo nginx -t

Common causes:

  • Missing semicolons
  • Incorrect file paths
  • Duplicate server blocks
  • Missing braces

PM2 Does Not Start After Reboot

Save processes:

pm2 save

Enable startup:

pm2 startup

Execute the generated command.

CORS Errors

Install CORS package:

npm install cors

Example:

const cors = require("cors");

app.use(cors({
  origin: "https://example.com"
}));

Restart the application:

pm2 restart backend-app

Firewall Blocking Access

Check firewall status:

sudo ufw status

Required ports:

22/tcp
80/tcp
443/tcp

Add missing rules if necessary.

Useful Diagnostic Commands

CommandPurposeTypical Usage
pm2 listShows all Node.js applications managed by PM2 and their status.Check whether your MERN backend is online, stopped, errored, or restarting repeatedly.
pm2 logsDisplays real-time logs from PM2 applications.Investigate application crashes, database connection errors, missing environment variables, API errors, etc.
systemctl status nginxChecks the Nginx web server status.Verify whether Nginx is running after configuration changes or server reboot.
systemctl status mongodChecks MongoDB service status.Confirm that MongoDB is running and accepting connections.
sudo nginx -tTests Nginx configuration syntax without restarting.Run before systemctl reload nginx to avoid breaking the website with invalid config.
sudo journalctl -xeShows recent system and service errors from the systemd journal.Investigate why a service failed to start or why the server is reporting system errors.
sudo ufw statusDisplays firewall rules and open ports.Verify whether ports such as 80, 443, 3000, or 5000 are allowed through the firewall.
ss -tulpnLists listening TCP/UDP ports and associated processes.Check whether Nginx, Node.js, MongoDB, MySQL, or other services are listening on the expected ports.
df -hShows disk usage in human-readable format.Determine whether the server is out of storage space, which can cause deployment failures.
free -mDisplays RAM and swap usage in MB.Diagnose memory shortages causing application crashes or PM2 restarts.

These commands help diagnose most deployment-related issues.

Security Best Practices

For production environments:

  • Keep Ubuntu packages updated.
  • Use strong database credentials.
  • Disable root SSH login where possible.
  • Use SSH keys instead of passwords.
  • Restrict MongoDB access to localhost.
  • Enable automatic SSL renewal.
  • Regularly back up MongoDB databases.
  • Monitor server logs and disk usage.
  • Use environment variables for secrets.
  • Never store credentials inside source code repositories.
Production environment security and maintenance

Maintenance and Monitoring

After deployment, ongoing maintenance is important.

Recommended tasks include:

Weekly

  • Review PM2 logs
  • Check disk usage
  • Verify application functionality

Monthly

  • Update server packages
  • Review firewall rules
  • Verify SSL renewal status

Regularly

  • Backup MongoDB databases
  • Monitor application performance
  • Review server security updates

Preventive maintenance significantly reduces downtime and security risks.

Conclusion

Deploying a MERN application in production involves much more than simply running a Node.js server. A complete deployment includes database configuration, process management, reverse proxy setup, firewall protection, DNS configuration, and SSL encryption.

By combining MongoDB, Node.js, PM2, Nginx, UFW, and Let’s Encrypt, you create a secure, maintainable, and scalable environment suitable for production workloads. Understanding how each component interacts with the others also makes troubleshooting easier and helps ensure long-term reliability of your applications.

R
Rajiv Raut

eHostingServer team member.

Leave a Reply

Your email address will not be published. Required fields are marked *