MERN Stack

Comprehensive Guide to Deploying and Managing MERN Stack Applications on RHEL-Based VPS

Published · Updated 17 min read
Knowledge base article
Contents (38 sections)

Introduction

The MERN stack (MongoDB, React, Express, Node.js) is a powerful and versatile full-stack framework for modern web applications. It offers a streamlined development process, scalability, and performance, making it a preferred choice for developers building everything from small-scale projects to enterprise solutions.

Key Components of the MERN Stack

  1. MongoDB: A NoSQL, document-based database designed for handling large datasets and providing horizontal scalability.
  2. Express.js: A minimal and flexible backend web framework for building RESTful APIs and web applications.
  3. React: A JavaScript library for creating dynamic, high-performance frontend user interfaces using reusable components.
  4. Node.js: A runtime environment that enables efficient, asynchronous backend development by handling concurrent requests seamlessly.

This guide focuses on deploying the MERN stack on RHEL-based VPS platforms such as Rocky Linux, AlmaLinux, and Oracle Linux, leveraging their stability, compatibility, and scalability.


Table of Contents

Introduction

  • Importance of the MERN Stack for Modern Web Development
  • Why Use a RHEL-Based VPS for Development
  • Prerequisites for Following This Guide

Provisioning Your RHEL-Based VPS

  • Setting Up a KVM VPS from Domain India
  • Configuring Basic Settings: Hostname, Timezone, and SSH Access
  • Updating and Upgrading RHEL Packages

Installing Essential Tools

  • Installing Node.js and npm
  • MongoDB Installation and Configuration
  • Setting Up Git for Version Control
  • Installing Development Tools: curl, wget, and Build Essentials
  • Additional Packages: pm2, yarn

Setting Up the MERN Stack

  • Installing and Configuring Express.js
  • Connecting MongoDB with Mongoose
  • Creating React Projects
  • Integrating Frontend and Backend for Full-Stack Functionality

Server Configuration for MERN Applications

  • Setting Up Environment Variables with .env Files
  • Configuring Reverse Proxy with Nginx
  • Setting Up Custom Domains for Your Application

Optimizing for Performance

  • Enabling Compression for Node.js Applications
  • MongoDB Optimization Techniques
  • Using PM2 for Load Balancing and Application Management

Securing Your RHEL-Based VPS

  • Implementing SSH Key-Based Authentication
  • Configuring firewalld for Application Security
  • Enabling MongoDB Authentication and IP Whitelisting
  • Installing SSL Certificates Using Let's Encrypt

Automating Backups

  • Backing Up MongoDB Databases Using mongodump
  • Automating File Backups with rsync and Cron Jobs
  • Configuring Cloud Backup Solutions

Monitoring and Maintenance

  • Using Monitoring Tools Like Nagios and Zabbix
  • Tracking Logs for Node.js and MongoDB
  • Setting Up Alerts for Critical Issues

Deploying Your Application

  • Testing Locally and Deploying to VPS
  • Using Git and GitHub for Code Management
  • Implementing CI/CD Pipelines for Faster Updates

Advanced Configurations

  • Setting Up Docker for MERN Projects
  • Using Redis for Caching
  • Configuring WebSockets for Real-Time Features

Troubleshooting Common Issues

  • Debugging MongoDB Errors
  • Resolving npm and Dependency Conflicts
  • Fixing React Build and Deployment Issues

Conclusion

  • Recap of Steps Taken
  • Best Practices for Ongoing Management
  • Useful Links and Resources

Appendix

  • Command Cheat Sheet for MERN Stack Development
  • Sample Configuration Files
  • Links to Official Documentation

Importance of the MERN Stack for Modern Web Development

The MERN stack combines the power of modern web technologies, making it one of the most popular choices for developers:

  1. Unified Development Environment: A single programming language (JavaScript) across the stack simplifies the development process and enhances productivity.
  2. Scalability: MongoDB's NoSQL model allows seamless handling of large datasets and high user loads.
  3. High Performance: Node.js provides non-blocking I/O, ensuring faster server responses even under heavy traffic.
  4. Rich Ecosystem: React's extensive library support and reusable components enable dynamic, high-performance frontends.

Why Use a RHEL-Based VPS for Development

Benefits of RHEL-Based Distributions

RHEL-based operating systems such as Rocky Linux, AlmaLinux, and Oracle Linux provide:

  • Stability and Security: Built for enterprise use, offering long-term support and robust security.
  • Compatibility: Binary compatibility with RHEL ensures seamless application deployment.
  • Cost Efficiency: Free alternatives like Rocky Linux and AlmaLinux eliminate subscription fees while providing enterprise-grade features.
  • Flexibility: Suitable for projects ranging from personal development to large-scale production environments.

Key RHEL-Based Distributions

  1. CentOS Stream (Development Only):

    • A rolling-release distribution providing upstream development access for RHEL. Ideal for testing and development environments but not recommended for production due to its rolling nature and potential instability.
  2. Rocky Linux (Development and Production):

    • A widely adopted, community-driven replacement for CentOS. Designed for production environments, offering robust long-term support (LTS) and stability.
  3. AlmaLinux (Development and Production):

    • A 1:1 binary-compatible RHEL alternative, backed by a non-profit foundation. Provides enterprise-grade features and is highly suitable for both development and production use.
  4. Oracle Linux (Development and Production):

    • Optimized for Oracle applications but also a viable choice for general-purpose development and production environments. Offers strong performance and enterprise support.
  5. ClearOS (Development and Small-Scale Production):

    • A user-friendly RHEL-based distribution aimed at small businesses. Simplified management makes it ideal for small-scale deployments, though not as robust for large-scale production.

Summary:

  • Development Only: CentOS Stream.
  • Development and Production: Rocky Linux, AlmaLinux, Oracle Linux.
  • Small-Scale Use: ClearOS.

With a RHEL VPS, you gain the flexibility to tailor your server for your specific MERN stack requirements, whether for development, production, or small-scale projects.


Prerequisites for Following This Guide

To proceed with the guide, you'll need the following:

  1. A RHEL 8.x or 9.x VPS: Ensure root or sudo access to the server.
  2. Basic Knowledge of Linux Commands: Familiarity with terminal operations is essential.
  3. SSH Client Installed: A tool like PuTTY (Windows) or the native SSH command (Linux/Mac) for accessing the VPS.
  4. A Domain India KVM VPS Plan: Purchase a VPS plan here.
  5. Internet Connection: Ensure the VPS has internet connectivity for package installations.

Provisioning Your RHEL VPS

Setting Up a KVM VPS from Domain India

  • Log in to your Domain India client area and purchase a suitable KVM VPS plan.
  • After setup, use the credentials sent by Domain India to connect to the VPS:
    ssh root@your-server-ip

Configuring Basic Settings: Hostname, Timezone, and SSH Access

Set the hostname to identify your VPS:
hostnamectl set-hostname my-mern-vps

Configure the timezone for your server:
timedatectl set-timezone Asia/Kolkata

Set Up SSH Key-Based Authentication on RHEL
Before disabling root login, configure SSH key-based access to ensure secure and uninterrupted access to the server.

Step 1: Create a Non-Root User

Log in as the root user:
ssh root@your-server-ip

Add a new user:
adduser newusername

Grant the user sudo privileges:
usermod -aG wheel newusername


Step 2: Generate an SSH Key Pair

On your local machine, generate an SSH key pair if you don't already have one:
ssh-keygen -t rsa -b 4096 -C "[email protected]"

Save the keys in the default location (e.g., ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub).


Step 3: Copy the SSH Key to the Server

Copy your public key to the server for the new user:
ssh-copy-id newusername@your-server-ip

Alternatively, manually copy the contents of the public key file (e.g., ~/.ssh/id_rsa.pub) to the following location on the server:
/home/newusername/.ssh/authorized_keys

Ensure the permissions for the .ssh directory and the authorized_keys file are correct:
chmod 700 /home/newusername/.ssh
chmod 600 /home/newusername/.ssh/authorized_keys

Secure the server by editing the SSH configuration file:

Open the file:
nano /etc/ssh/sshd_config

Disable root login by modifying:
PermitRootLogin no

Save and restart the SSH service:
systemctl restart sshd


Updating and Upgrading RHEL Packages

Update your system packages to the latest versions:
yum update -y

Install additional tools and repositories for better compatibility:
yum install -y epel-release vim wget curl


Installing Essential Tools

Step-by-Step Guide to Installing Node.js and npm

Enable the NodeSource repository:
curl -sL https://rpm.nodesource.com/setup_16.x | bash -

Install Node.js and npm:
yum install -y nodejs

Verify the installation:

Check Node.js version:
node -v

Check npm version:
npm -v


MongoDB Installation on RHEL

Step 1: Add the MongoDB Repository

Create and edit the repository file for MongoDB 6.0:

markup
cat <<EOF | tee /etc/yum.repos.d/mongodb-org-6.0.repo
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/8/mongodb-org/6.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc
EOF

Step 2: Install MongoDB

Install MongoDB using the yum package manager:

markup
sudo yum install -y mongodb-org

Step 3: Start and Enable MongoDB

Enable MongoDB to start automatically and verify it is running:

markup
sudo systemctl start mongod
sudo systemctl enable mongod
sudo systemctl status mongod

Step 4: Test the Installation

Verify that MongoDB is installed and operational:

markup
mongo --version

Additional Notes:

  1. Ensure Repository Accuracy: The repository URL should match your server's architecture and OS version. The above example is for RHEL 8.x and 64-bit architecture.
  2. SELinux and Firewall Configuration: If SELinux is enabled or firewalld is in use, ensure the appropriate ports (default: 27017) are open and policies are configured correctly.


Installing Git for Version Control

Install Git:
yum install -y git

Verify the installation:
git --version

Configure Git with your details:

Set your name:
git config --global user.name "Your Name"

Set your email:
git config --global user.email "[email protected]"


Setting Up Development Tools: cURL, wget, and Build Essentials

Install the required tools:
yum install -y curl wget gcc gcc-c++ make

Verify installations:

Check cURL version:
curl --version

Check wget version:
wget --version


Installing Additional Packages: pm2, yarn

Install pm2 globally using npm:
npm install -g pm2

Install yarn package manager:
npm install -g yarn

Verify installations:

Check pm2 version:
pm2 -v

Check yarn version:
yarn -v

Setting Up the MERN Stack

Installing and Configuring Express.js for Backend

Create a directory for your backend application:
mkdir backend && cd backend

Initialize a new Node.js project:
npm init -y

Install Express.js:
npm install express

Create a basic Express.js server in index.js:

markup
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
 res.send('Hello, MERN Stack!');
});

app.listen(PORT, () => {
 console.log(`Server is running on port ${PORT}`);
});

Run the server:
node index.js


Connecting MongoDB with Mongoose

Install Mongoose in your backend project:
npm install mongoose

Connect to MongoDB in index.js:

markup
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/mern-stack', {
 useNewUrlParser: true,
 useUnifiedTopology: true,
})
.then(() => console.log('MongoDB connected successfully'))
.catch(err => console.log('MongoDB connection error:', err));

Test the connection by restarting the server:
node index.js


Creating React Projects and Building Frontend Applications

  1. Install create-react-app globally (optional):
    npm install -g create-react-app

  2. Create a new React application:
    npx create-react-app frontend

  3. Navigate to the React project directory:
    cd frontend

  4. Start the development server:
    npm start

  5. Customize the default React component:
    Modify the file src/App.js with the following code:

markup
function App() {
 return (
 <div className="App">
 <h1>Welcome to the MERN Stack</h1>
 </div>
 );
}

export default App;
Your React app will be live at http://localhost:3000.

Integrating Frontend and Backend for Full-Stack Functionality

  1. Add a proxy to connect React to the backend:
    Open the file frontend/package.json and add the following line:

    "proxy": "http://localhost:5000"

  2. Update the backend to serve React files in production:
    Modify index.js in the backend directory with the following code:

markup
const path = require('path');
const express = require('express');
const app = express();

// Serve React static files
app.use(express.static(path.join(__dirname, '../frontend/build')));

// Handle all unmatched routes with the React app
app.get('*', (req, res) => {
 res.sendFile(path.join(__dirname, '../frontend/build', 'index.html'));
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
  • Build the React app for production:
    Run the following command inside the frontend directory:

    npm run build

  • Start the integrated application:
    Run the backend server with the following command:

    node index.js

  • Install the dotenv package:
    npm install dotenv

    Load the environment variables in index.js:

    markup
    require('dotenv').config();
    const PORT = process.env.PORT;
    const MONGO_URI = process.env.MONGO_URI;
    

    Configuring Reverse Proxy with Nginx

    Install Nginx:
    yum install -y nginx

    Create a configuration file for your application:

    Open /etc/nginx/conf.d/mern-stack.conf and add:

    markup
    server {
     listen 80;
    
     server_name your-domain.com;
    
     location / {
     proxy_pass http://localhost:5000;
     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;
     }
    }
    

    Restart Nginx to apply changes:
    systemctl restart nginx

    Setting Up Custom Domains for Your App

    Point your domain's DNS to the VPS IP address.

    Update the Nginx server_name directive to use your domain (e.g., your-domain.com).

    Test the domain in a browser to ensure it resolves correctly.


    Optimizing for Performance

    Enabling Compression for Node.js Applications

    Install the compression package:
    npm install compression

    Use the package in your Express.js app:

    markup
    const compression = require('compression');
    app.use(compression());
    

    Restart your server to apply compression.

    MongoDB Optimization Techniques for Scalability

    Enable journaling for better performance and crash recovery:
    Edit /etc/mongod.conf and ensure the following is enabled:

    markup
    storage:
     journal:
     enabled: true
    

    Index frequently queried fields for faster lookups:

    markup
    const schema = new mongoose.Schema({
     name: { type: String, index: true },
    });
    

    Monitor MongoDB performance using built-in tools:
    Use mongostat and mongotop commands to identify bottlenecks.


    Using PM2 for Load Balancing and Application Management

    Start your application with PM2:
    pm2 start index.js

    Monitor running applications:
    pm2 list

    Enable application auto-restart on server reboot:
    pm2 startup
    pm2 save

    Securing Your RHEL VPS

    Implementing SSH Key-Based Authentication

    Generate an SSH key pair on your local machine:
    ssh-keygen -t rsa -b 4096 -C "[email protected]"

    Copy the public key to the VPS:
    ssh-copy-id root@your-server-ip

    Test the SSH connection to ensure it works:
    ssh root@your-server-ip

    Disable password authentication for added security:

    Open the SSH configuration file:
    nano /etc/ssh/sshd_config

    Modify or add:
    PasswordAuthentication no

    Restart SSH:
    systemctl restart sshd


    Configuring firewalld for Application and Database Security

    Install firewalld:
    yum install -y firewalld

    Start and enable firewalld:
    systemctl start firewalld
    systemctl enable firewalld

    Open necessary ports:

    HTTP (80) and HTTPS (443):
    firewall-cmd --permanent --add-service=http
    firewall-cmd --permanent --add-service=https

    MongoDB (27017):
    firewall-cmd --permanent --add-port=27017/tcp

    Reload the firewall to apply changes:
    firewall-cmd --reload


    Enabling MongoDB Authentication and IP Whitelisting

    Open MongoDB configuration:
    nano /etc/mongod.conf

    Enable authentication by adding:

    markup
    security:
     authorization: "enabled"
    

    Restart MongoDB:
    systemctl restart mongod

    Create an admin user:

    markup
    use admin
    db.createUser({
     user: "admin",
     pwd: "strongpassword",
     roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
    });
    

    Restrict MongoDB access to specific IPs by editing the bind IP:

    markup
    net:
     bindIp: 127.0.0.1,192.168.1.100
    

    Restart MongoDB to apply changes:
    systemctl restart mongod

    Installing SSL Certificates Using Let's Encrypt

    Install Certbot:
    yum install -y certbot python3-certbot-nginx

    Obtain an SSL certificate:
    certbot --nginx -d your-domain.com -d www.your-domain.com

    Test the SSL configuration:
    certbot renew --dry-run

    Set up automatic renewal:
    systemctl enable certbot-renew.timer


    Automating Backups

    Backing Up MongoDB Databases Using mongodump

    Create a directory for backups:
    mkdir -p /backups/mongodb

    Run mongodump to create a backup:
    mongodump --out /backups/mongodb/$(date +%F)

    Schedule daily backups using cron:

    Open the crontab:
    crontab -e

    Add the following line:
    0 2 * * * mongodump --out /backups/mongodb/$(date +\%F)


    Automating File Backups with rsync and Cron Jobs

    Install rsync:
    yum install -y rsync

    Create a backup script:

    markup
    rsync -avz /var/www/html /backups/files/
    

    Schedule the script with cron:

    Open the crontab:
    crontab -e

    Add the following line:
    0 3 * * * rsync -avz /var/www/html /backups/files/


    Configuring Cloud Backup Solutions

    Install a cloud storage CLI, such as AWS CLI:
    yum install -y aws-cli

    Configure the AWS CLI with your credentials:
    aws configure

    Use the AWS CLI to upload backups to an S3 bucket:

    markup
    aws s3 cp /backups/mongodb s3://your-s3-bucket/ --recursive
    

    Automate this with a cron job.

    Monitoring and Maintenance

    Using Monitoring Tools Like Nagios and Zabbix

    Install Nagios or Zabbix (choose one based on preference).

    Nagios:
    yum install -y nagios

    For a step-by-step guide to setting up and optimizing Nagios, check out our article on Mastering Nagios: A Comprehensive Guide to Server Monitoring.

    Zabbix:
    Follow the official Zabbix installation guide.

    Configure monitoring for critical services like Node.js, MongoDB, and Nginx.


    Tracking Logs for Node.js and MongoDB Applications

    View Node.js application logs:
    pm2 logs

    Monitor MongoDB logs:
    tail -f /var/log/mongodb/mongod.log

    Use a log analyzer like ELK Stack (Elasticsearch, Logstash, Kibana) for detailed insights.


    Setting Up Alerts for Critical Errors

    Use a monitoring tool with alerting capabilities, such as Nagios or Zabbix.

    Configure email or SMS notifications for critical events like high CPU usage, memory usage, or database errors.

    Test alerts to ensure they trigger correctly during failures.


    Deploying Your Application

    Testing Locally and Deploying to the VPS

    Test Locally:

    Ensure your application works on your local environment.

    Test frontend and backend APIs to ensure functionality.

    Run:
    npm start for React.
    node index.js or pm2 start index.js for Node.js.

    Prepare for Deployment:

    Build the frontend:
    cd frontend && npm run build

    Copy the build/ folder to the backend directory.

    Deploy to VPS:

    Copy project files to the VPS using SCP:
    scp -r /path/to/project root@your-server-ip:/var/www/mern-app

    Set permissions:
    chmod -R 755 /var/www/mern-app

    Start the application:
    pm2 start index.js


    Using Git and GitHub for Code Management

    Initialize a Git Repository:

    Inside your project directory:
    git init

    Push Code to GitHub:

    Add files to Git:
    git add .

    Commit changes:
    git commit -m "Initial commit"

    Push to a GitHub repository:

    markup
    git remote add origin https://github.com/username/repo.git 
    git push -u origin main
    

    Deploy Using Git on VPS:

    Clone the repository:
    git clone https://github.com/username/repo.git /var/www/mern-app

    Pull updates when necessary:
    cd /var/www/mern-app && git pull origin main

    Implementing CI/CD Pipelines for Faster Updates

    Use GitHub Actions for CI/CD:

    Create a .github/workflows/deploy.yml file:

    markup
    name: Deploy Application
    
    on:
     push:
     branches:
     - main
    
    jobs:
     deploy:
     runs-on: ubuntu-latest
     steps:
     - name: Checkout code
     uses: actions/checkout@v2
    
     - name: Deploy to Server
     run: |
     scp -r . root@your-server-ip:/var/www/mern-app
     ssh root@your-server-ip "pm2 restart index.js"
    

    Test the Pipeline:

    Push code to the main branch and verify the deployment.


    Advanced Configurations

    Setting Up Docker Containers for MERN Projects

    Install Docker:
    yum install -y docker
    systemctl start docker && systemctl enable docker

    Create a Dockerfile:

    markup
    FROM node:16
    WORKDIR /app
    COPY . .
    RUN npm install
    CMD ["node", "index.js"]
    

    Build and Run the Docker Container:

    Build:
    docker build -t mern-app .

    Run:
    docker run -d -p 3000:3000 mern-app


    Using Redis for Caching

    Install Redis:
    yum install -y redis
    systemctl start redis && systemctl enable redis

    Integrate Redis in Node.js:

    Install the package:
    npm install redis

    Use it in your app:

    markup
    const redis = require('redis');
    const client = redis.createClient();
    client.on('connect', () => console.log('Connected to Redis'));
    


    Configuring WebSockets for Real-Time Applications

    Install socket.io:
    npm install socket.io

    Integrate WebSockets in Node.js:

    markup
    const io = require('socket.io')(server);
    io.on('connection', (socket) => {
     console.log('A user connected');
     socket.on('disconnect', () => console.log('A user disconnected'));
    });
    

    Test with a WebSocket client to ensure real-time communication works.


    Troubleshooting Common Issues

    Debugging Common MongoDB Errors

    Connection Errors:
    Ensure MongoDB is running:
    systemctl status mongod
    Verify the connection string:
    mongodb://localhost:27017/your-database

    Authentication Errors:
    Use the correct user credentials and roles.


    Resolving npm and Dependency Conflicts

    Delete node_modules and reinstall dependencies:
    rm -rf node_modules && npm install

    Use a specific version of a conflicting package:
    npm install package-name@version


    Handling React Build and Deployment Errors

    Ensure correct homepage in package.json:

    markup
    "homepage": "https://your-domain.com",
    

    Rebuild React after updates:
    npm run build

    Verify React files are served correctly by your backend.


    Conclusion

    Recap of Steps Taken

    Provisioned and configured a RHEL VPS.

    Set up the MERN stack with MongoDB, React, Express, and Node.js.

    Secured the server using SSH keys, firewalld, and SSL.

    Deployed and tested the application.

    Optimized for performance and set up monitoring.


    Best Practices for Ongoing Management

    Regularly update your packages and system.

    Use CI/CD pipelines for seamless updates.

    Monitor application logs and server performance.

    Automate backups to ensure data safety.



    Appendix

    Command Cheat Sheet for MERN Stack Development

    Start MongoDB:
    systemctl start mongod

    Start React:
    npm start

    Deploy Node.js app with PM2:
    pm2 start index.js


    Sample Configuration Files

    Nginx Configuration:

    markup
    server {
     listen 80;
     server_name your-domain.com;
     location / {
     proxy_pass http://localhost:5000;
     }
    }
    

    MongoDB Configuration:

    markup
    security:
     authorization: "enabled"
    net:
     bindIp: 127.0.0.1
    

    Key References from Existing Articles

    Before diving into the guide, here are some references to relevant articles that provide foundational knowledge for this guide:

    Was this article helpful?

    Your answer helps us decide what to improve next.

    Still need help? Open a support ticket and our team will reply.

    Prefer an app? Add this site to your home screen.Get the app