How to Set Up a MERN Stack on AlmaLinux

By Anurag Singh

Updated on May 27, 2025

How to Set Up a MERN Stack on AlmaLinux

In this tutorial, we'll learn how to set up a MERN stack on AlmaLinux 9. 

We’re going to walk through every step of standing up a full MERN stack on AlmaLinux 9—MongoDB for data storage, Express.js for our API, React for the front-end, and Node.js as our runtime. By the end, we’ll have a working “Hello World” API talking to a React app. Let’s dive in.

Prerequisites

Before we begin, let’s ensure we have the following in place:

How to Set Up a MERN Stack on AlmaLinux

1. Prepare and Update Our System

First, let’s make sure our AlmaLinux 9 server is up-to-date and has the tools we need.

sudo dnf update -y
sudo dnf install -y git curl wget
  • dnf update brings all packages to the latest security and bug-fix versions.
  • We add git, curl, and wget since we’ll fetch repos and installers later.

2. Install Node.js (LTS)

We want a stable, long-term support version—at the moment that’s Node.js 24.x. We’ll pull it from NodeSource.

curl -fsSL https://rpm.nodesource.com/setup_24.x | sudo bash -
sudo dnf install -y nodejs
  • The script configures the NodeSource repo for AlmaLinux 9.
  • After that, dnf install nodejs gives us both node and npm.

Verify versions:

node -v   # should print v24.x.x
npm -v    # will print npm 11.x or similar

3. Install MongoDB Community Edition

AlmaLinux 9 doesn’t ship MongoDB by default, so we’ll add the official MongoDB repo for RPM-based distros.

cat <<EOF | sudo tee /etc/yum.repos.d/mongodb-org-8.0.repo
[mongodb-org-8.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/8.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://pgp.mongodb.com/server-8.0.asc
EOF

Now, install MongoDB and start the service using following command: 

There is some issue the mongodb-mongosh and OpenSSL v3. By default mongodb-mongosh gets install with mongodb-org package. We are not sure what is the issue but we can go with following solution:

We need to install mongodb-mongosh-shared-openssl3 first and than mongodb-org

dnf install -qy mongodb-mongosh-shared-openssl3
sudo dnf install -y mongodb-org
sudo systemctl enable --now mongod
  • We define a new repo file pointing at MongoDB 8.0 for RHEL 9 (compatible with AlmaLinux).
  • install mongodb-org brings in the server, shell, and tools.
  • enable --now mongod starts MongoDB and ensures it launches on boot.

This way we can use mongosh command and get into database cli.

Quick test:

mongosh --eval 'db.runCommand({ connectionStatus: 1 })'

You should see “ok: 1” indicating the server is running.

{
  authInfo: { authenticatedUsers: [], authenticatedUserRoles: [] },
  ok: 1
}

4. Scaffold the MERN Project Structure

Let’s create a folder to house both backend and frontend:

mkdir mern-almalinux && cd mern-almalinux
mkdir backend frontend

Keeping backend and frontend separate simplifies development and deployment.

5. Build the Express+Node.js API

Inside backend, initialize npm and install our dependencies:

cd backend
npm init -y
npm install express mongoose cors dotenv
npm install --save-dev nodemon
  • express: minimal web framework
  • mongoose: MongoDB ODM for defining schemas and models
  • cors: enable Cross-Origin Resource Sharing (so React on another port can talk to us)
  • dotenv: load environment variables
  • nodemon: auto-reload server during development

Create a file structure:

backend/
├── .env
├── server.js
└── models/
    └── Item.js

.env (do not commit this to git):

nano .env

Add following content:

PORT=5000
MONGO_URI=mongodb://localhost:27017/mern_app

models/Item.js:

mkdir models
nano models/Item.js

Add following code:

const mongoose = require('mongoose');
const ItemSchema = new mongoose.Schema({
  name: { type: String, required: true },
  createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Item', ItemSchema);

server.js:

nano server.js

Add following code:

require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const Item = require('./models/Item');

const app = express();
app.use(cors());
app.use(express.json());

// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true
})
.then(() => console.log('✅ Connected to MongoDB'))
.catch(err => console.error('❌ MongoDB connection error:', err));

// Routes
app.get('/api/items', async (req, res) => {
  const items = await Item.find().sort('-createdAt');
  res.json(items);
});

app.post('/api/items', async (req, res) => {
  const newItem = new Item({ name: req.body.name });
  const saved = await newItem.save();
  res.status(201).json(saved);
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`));

Add a script to package.json for development:

nano package.json

Adjust scripts looks like following:

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

Run npm run dev to verify the API is live on http://localhost:5000.

6. Create the React Frontend (v19)

In frontend, we’ll use Vite to scaffold React 19 (released December 5, 2024):

cd ../frontend
npm create vite@latest . -- --template react
npm install

Vite gives us a fast development server and bundles with optimized defaults.

Open frontend/package.json, adjust the dev script if needed, then:

In src/App.jsx, replace with a simple fetch UI:

nano src/App.jsx

Adjust current code with following code:

import { useState, useEffect } from 'react';

function App() {
  const [items, setItems] = useState([]);
  const [name, setName] = useState('');

  useEffect(() => {
    fetch('http://localhost:5000/api/items')
      .then(res => res.json())
      .then(setItems);
  }, []);

  const addItem = async () => {
    if (!name) return;
    const res = await fetch('http://localhost:5000/api/items', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name })
    });
    const created = await res.json();
    setItems(prev => [created, ...prev]);
    setName('');
  };

  return (
    <div className="container">
      <h1>Our MERN App</h1>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
        placeholder="New item name"
      />
      <button onClick={addItem}>Add Item</button>
      <ul>
        {items.map(it => (
          <li key={it._id}>{it.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

Ensure CORS is enabled in the Express server (we added app.use(cors()) above).

Run the React dev server:

npm run dev

Your frontend now lives at http://localhost:5173 (or similar Vite port).

7. Running Backend & Frontend Together

During development, keep two terminals open:

Backend:

cd mern-almalinux/backend
npm run dev

Frontend:

cd mern-almalinux/frontend
npm run dev

This lets us iterate quickly. For production, we’d build the React app (npm run build) and serve it via Express or a dedicated static‐hosting service.

To access your application on your server's public IP:

npm run dev -- --host 0.0.0.0

8. Next Steps & Best Practices

  • Environment Variables: Never commit secrets.
  • Error Handling & Validation: Use middleware for robust APIs.
  • Security: Sanitize inputs and configure HTTPS.
  • Deployment: Consider Dockerizing each component.
  • Scaling: For production MongoDB, explore Atlas or replica sets.

In this tutorial, we've learnt how to set up a MERN stack on AlmaLinux 9. With these steps, we’ve built a modern MERN stack on AlmaLinux 9, using the latest Node.js 24 LTS, MongoDB 8.x, and React 19. Happy coding—and here’s to building great applications together!