How To Install MERN Stack on AlmaLinux 10
Building modern web applications requires a robust technology stack that combines efficiency, scalability, and developer productivity. The MERN Stack—MongoDB, Express.js, React.js, and Node.js—has emerged as one of the most popular full-stack JavaScript frameworks for creating dynamic, data-driven applications. When paired with AlmaLinux 10, a stable, enterprise-grade Linux distribution that serves as a community-driven alternative to Red Hat Enterprise Linux, developers gain a powerful foundation for both development and production environments. AlmaLinux 10 delivers long-term support, enhanced security features, and exceptional performance characteristics that make it ideal for hosting MERN applications. This comprehensive guide walks through the complete installation process, from initial system preparation through deploying a production-ready MERN application on AlmaLinux 10.
Prerequisites and System Requirements
Hardware Requirements
Setting up a functional MERN Stack environment demands adequate system resources. The minimum hardware specifications include a 64-bit x86_64 processor architecture, which AlmaLinux 10 requires for compatibility. Allocate at least 2 GB of RAM for basic development work, though 4 GB or more is strongly recommended when running all MERN components simultaneously. Storage requirements begin at 20 GB of available disk space, with 40 GB or greater preferred for production deployments that will accumulate data, logs, and application assets over time. Running MongoDB, Node.js development servers, React build processes, and various development tools concurrently creates meaningful resource demands that underscore the importance of adequate provisioning.
Software Prerequisites
Begin with a fresh AlmaLinux 10 installation with full root access or a user account configured with sudo privileges. Ensure all system packages are current and an active internet connection exists for downloading required software components. Familiarity with basic Linux command-line operations, text editors such as nano or vi, and fundamental JavaScript concepts will facilitate the installation process. Understanding of client-server architecture and REST API principles proves beneficial but is not strictly mandatory for following this tutorial.
Security Considerations
Plan firewall configurations before beginning installation. MongoDB typically operates on port 27017, while Node.js development servers commonly use ports 3000 and 5000. Production deployments may require additional port configurations depending on the chosen hosting architecture and reverse proxy setup.
Step 1: Update AlmaLinux 10 System
System updates form the foundation of secure server management. Open a terminal session and execute the complete system update command:
sudo dnf update -y
This command refreshes package repositories and installs all available security patches and software updates. The -y
flag automatically confirms installation prompts, streamlining the update process. System updates protect against known vulnerabilities and ensure package compatibility with newer software versions.
Install essential development tools required for compiling native Node.js modules and building software from source:
sudo dnf groupinstall "Development Tools" -y
This group installation provides gcc, g++, make, and other compilation utilities. Additionally, install fundamental utilities:
sudo dnf install git curl wget -y
Git enables version control for project files, while curl and wget facilitate downloading installation scripts and software packages. Verify successful installation by checking version information:
git --version
curl --version
Expected output displays version numbers, confirming proper installation.
Step 2: Install Node.js and NPM
NodeSource Repository Method
AlmaLinux default repositories typically contain older Node.js versions. The NodeSource repository provides access to current Long-Term Support releases. Download and execute the NodeSource setup script for Node.js 24.x LTS:
curl -fsSL https://rpm.nodesource.com/setup_24.x | sudo bash -
This script configures the NodeSource repository on the system. The -fsSL
flags ensure silent operation with failed request handling and redirect following. After repository configuration completes, install Node.js:
sudo dnf install nodejs -y
This single package includes both the Node.js runtime and NPM package manager. Using NodeSource repositories instead of default AlmaLinux packages ensures access to modern JavaScript features, performance improvements, and active security maintenance.
Verification
Confirm successful installation by checking installed versions:
node -v
npm -v
Expected output shows Node.js version 24.x and NPM version 10.x or higher. Version mismatches or command-not-found errors indicate installation problems requiring troubleshooting.
Global NPM Configuration
Avoid permission-related issues during global package installation by configuring NPM to use a user-owned directory:
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
This configuration redirects global packages to the home directory, eliminating the need for sudo when installing global utilities.
Step 3: Install and Configure MongoDB
Adding MongoDB Repository
MongoDB requires manual repository configuration on AlmaLinux 10. Create a new repository file:
sudo nano /etc/yum.repos.d/mongodb-org-8.0.repo
Add the following repository configuration for MongoDB 8.0:
[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
Save and exit the editor. This configuration specifies the MongoDB package source, enables GPG signature verification for security, and provides the public key location for package authentication.
Installing MongoDB Server
Execute the MongoDB installation command:
sudo dnf install mongodb-org -y
This meta-package installs multiple components: mongodb-org-server (the database daemon), mongodb-org-mongos (sharding router), mongodb-org-shell (command-line client), and mongodb-org-tools (import/export utilities).
Starting and Enabling MongoDB
Start the MongoDB service immediately:
sudo systemctl start mongod
Enable automatic startup on system boot:
sudo systemctl enable mongod
Verify the service is running properly:
sudo systemctl status mongod
Active status with “running” confirmation indicates successful startup. Press ‘q’ to exit the status display.
Securing MongoDB
Fresh MongoDB installations lack authentication, creating security vulnerabilities. Connect to MongoDB shell:
mongosh
Create an administrative user:
use admin
db.createUser({
user: "admin",
pwd: "StrongPassword123!",
roles: [{role: "userAdminAnyDatabase", dbOwner: "admin"}]
})
Replace “StrongPassword123!” with a secure password. Exit the shell with exit
. Enable authentication by editing the MongoDB configuration:
sudo nano /etc/mongod.conf
Locate the security section and uncomment or add:
security:
authorization: enabled
Save the file and restart MongoDB:
sudo systemctl restart mongod
Test authenticated access:
mongosh -u admin -p
Enter the password when prompted. Successful authentication confirms proper security configuration.
Firewall Configuration
If remote MongoDB access is required, open the appropriate firewall port:
sudo firewall-cmd --permanent --add-port=27017/tcp
sudo firewall-cmd --reload
For development environments, restricting MongoDB to localhost connections is recommended for enhanced security.
Step 4: Set Up Project Directory Structure
Organized project structure facilitates maintenance and scalability. Create a main project directory:
mkdir -p ~/mern-almalinux
cd ~/mern-almalinux
The MERN architecture separates concerns between backend API logic and frontend user interface. This separation enables independent development, testing, and deployment of each layer. Initialize version control:
git init
Create a .gitignore
file to exclude sensitive and unnecessary files:
nano .gitignore
Add the following entries:
node_modules/
.env
build/
dist/
*.log
This prevents committing dependencies, environment variables, and build artifacts to version control.
Step 5: Build the Backend (Express + Node.js)
Initialize Backend Directory
Create and navigate to the backend folder:
mkdir backend
cd backend
Initialize a new Node.js project:
npm init -y
The -y
flag accepts default values, generating a package.json file that manages project metadata and dependencies.
Install Backend Dependencies
Install core backend packages:
npm install express mongoose cors dotenv
Express.js provides the web application framework, Mongoose offers elegant MongoDB object modeling, CORS enables cross-origin resource sharing, and dotenv loads environment variables from files. Install development dependency:
npm install --save-dev nodemon
Nodemon automatically restarts the server when file changes are detected, improving development workflow.
Environment Variables Configuration
Create an environment file:
nano .env
Add configuration variables:
PORT=5000
MONGO_URI=mongodb://admin:StrongPassword123!@localhost:27017/merndb?authSource=admin
Replace the password with the actual MongoDB admin password. Never commit this file to version control repositories.
Create Mongoose Model
Establish a models directory:
mkdir models
nano models/Item.js
Define a sample data model:
const mongoose = require('mongoose');
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
description: {
type: String,
default: ''
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Item', ItemSchema);
This schema defines an Item collection with name, description, and timestamp fields. Mongoose handles validation and database interactions.
Build Express Server
Create the main server file:
nano server.js
Implement the Express application:
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const Item = require('./models/Item');
const app = express();
const PORT = process.env.PORT || 5000;
// Middleware
app.use(cors());
app.use(express.json());
// MongoDB Connection
mongoose.connect(process.env.MONGO_URI)
.then(() => console.log('MongoDB connected successfully'))
.catch(err => console.error('MongoDB connection error:', err));
// Routes
app.get('/api/items', async (req, res) => {
try {
const items = await Item.find().sort({ createdAt: -1 });
res.json(items);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
app.post('/api/items', async (req, res) => {
try {
const item = new Item({
name: req.body.name,
description: req.body.description
});
const newItem = await item.save();
res.status(201).json(newItem);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
app.delete('/api/items/:id', async (req, res) => {
try {
await Item.findByIdAndDelete(req.params.id);
res.json({ message: 'Item deleted' });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Start Server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
This server implements RESTful endpoints for creating, reading, and deleting items.
Package.json Scripts
Edit package.json to add convenience scripts:
nano package.json
Modify the scripts section:
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
The dev script enables hot-reloading during development.
Step 6: Create the Frontend (React.js)
Initialize React Application
Navigate to the project root and create a React application using Vite:
cd ~/mern-almalinux
npm create vite@latest frontend -- --template react
Vite provides faster build times and superior development experience compared to traditional Create React App. Navigate to the frontend directory:
cd frontend
npm install
This installs all React dependencies.
Install Frontend Dependencies
Add Axios for HTTP requests:
npm install axios
Axios simplifies API communication with promise-based syntax.
Configure API Connection
Create an API service file:
mkdir src/services
nano src/services/api.js
Configure the Axios instance:
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:5000/api'
});
export default api;
This centralized configuration simplifies endpoint management.
Build React Components
Edit the main App component:
nano src/App.jsx
Implement the application logic:
import { useState, useEffect } from 'react';
import api from './services/api';
import './App.css';
function App() {
const [items, setItems] = useState([]);
const [name, setName] = useState('');
const [description, setDescription] = useState('');
useEffect(() => {
fetchItems();
}, []);
const fetchItems = async () => {
try {
const response = await api.get('/items');
setItems(response.data);
} catch (error) {
console.error('Error fetching items:', error);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
await api.post('/items', { name, description });
setName('');
setDescription('');
fetchItems();
} catch (error) {
console.error('Error creating item:', error);
}
};
const handleDelete = async (id) => {
try {
await api.delete(`/items/${id}`);
fetchItems();
} catch (error) {
console.error('Error deleting item:', error);
}
};
return (
<div className="container">
<h1>MERN Stack on AlmaLinux 10</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Item name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<input
type="text"
placeholder="Description"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
<button type="submit">Add Item</button>
</form>
<div className="items-list">
<h2>Items</h2>
{items.map(item => (
<div key={item._id} className="item">
<h3>{item.name}</h3>
<p>{item.description}</p>
<small>{new Date(item.createdAt).toLocaleString()}</small>
<button onClick={() => handleDelete(item._id)}>Delete</button>
</div>
))}
</div>
</div>
);
}
export default App;
This component manages state with React hooks, fetches data from the backend, and provides a user interface for CRUD operations.
Styling Considerations
Add basic styling to App.css:
nano src/App.css
Include responsive, clean styles that enhance usability without overwhelming complexity.
CORS Configuration
The Express CORS middleware enables the React development server (typically port 5173) to communicate with the backend on port 5000. Alternative approaches include configuring Vite’s proxy settings to avoid CORS entirely during development.
Step 7: Testing the MERN Application
Running Backend Server
Open a terminal session and start the backend:
cd ~/mern-almalinux/backend
npm run dev
Successful output displays “MongoDB connected successfully” and “Server running on port 5000”. Leave this terminal running.
Running Frontend Server
Open a second terminal and start React:
cd ~/mern-almalinux/frontend
npm run dev -- --host 0.0.0.0
The --host
flag allows external access, useful for server deployments. Vite displays the local access URL, typically http://localhost:5173
.
Testing CRUD Operations
Access the application through a web browser. Test the CREATE operation by entering an item name and clicking “Add Item”. The interface should refresh, displaying the new entry retrieved from MongoDB. This demonstrates the complete data flow: React frontend → Express API → MongoDB database → Express response → React update. Test DELETE functionality by clicking the delete button on any item. Verify data persists by stopping and restarting both servers.
MongoDB Verification
Connect to MongoDB shell:
mongosh -u admin -p
Query the database directly:
use merndb
db.items.find().pretty()
This displays all stored documents, confirming successful data persistence.
Step 8: Production Deployment with PM2
Installing PM2 Process Manager
PM2 ensures Node.js applications remain running, automatically restart after crashes, and survive system reboots. Install PM2 globally:
npm install pm2@latest -g
Verify installation:
pm2 --version
Building Production React App
Navigate to the frontend directory and create an optimized production build:
cd ~/mern-almalinux/frontend
npm run build
Vite generates minified, optimized static files in the dist
directory.
Configuring Express for Production
Modify the backend to serve React static files. Edit server.js:
cd ~/mern-almalinux/backend
nano server.js
Add before the route definitions:
const path = require('path');
// Serve static files in production
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '../frontend/dist')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../frontend/dist/index.html'));
});
}
This configuration serves the React application from Express, eliminating the need for separate frontend hosting.
Starting Backend with PM2
Launch the application with PM2:
cd ~/mern-almalinux/backend
NODE_ENV=production pm2 start server.js --name mern-backend
View running processes:
pm2 list
The application appears with status “online”.
PM2 Configuration
Save the current PM2 configuration:
pm2 save
Generate a startup script:
pm2 startup
Execute the command PM2 displays to enable automatic startup. This ensures the application restarts after system reboots.
Process Monitoring
Monitor application performance in real-time:
pm2 monit
View application logs:
pm2 logs mern-backend
Restart the application:
pm2 restart mern-backend
Reload with zero downtime:
pm2 reload mern-backend
Stop the application:
pm2 stop mern-backend
These commands provide comprehensive process management capabilities.
Step 9: Security Best Practices
Environment Security
Never expose sensitive credentials in code or version control. Environment files remain local only. Use environment-specific configurations for development, staging, and production environments. Consider using secret management services for production deployments.
MongoDB Security
Implement strong password policies requiring complexity, length, and regular rotation. Configure MongoDB’s bind_ip directive to restrict network access:
net:
bindIp: 127.0.0.1
This limits connections to localhost only. Implement regular backup strategies using mongodump:
mongodump --uri="mongodb://admin:password@localhost:27017" --out=/backup/mongodb
Application Security
Validate and sanitize all user inputs to prevent injection attacks. Install and configure Helmet.js for Express:
npm install helmet
Add to server.js:
const helmet = require('helmet');
app.use(helmet());
Implement rate limiting to prevent abuse:
npm install express-rate-limit
Configure limits based on endpoint sensitivity. Deploy HTTPS with SSL/TLS certificates in production using Let’s Encrypt or commercial certificate authorities.
Firewall Configuration
Configure AlmaLinux firewall for production:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --remove-port=3000/tcp
sudo firewall-cmd --permanent --remove-port=5000/tcp
sudo firewall-cmd --reload
Expose only necessary ports while closing development-specific ports.
Updates and Maintenance
Regularly audit dependencies for vulnerabilities:
npm audit
Address identified issues:
npm audit fix
Update packages systematically:
npm update
Keep AlmaLinux system packages current:
sudo dnf update -y
Establish maintenance schedules for security patches and updates.
Step 10: Troubleshooting Common Issues
Connection Problems
MongoDB connection failures often stem from incorrect connection strings. Verify service status:
sudo systemctl status mongod
Ensure authentication credentials match those created during setup. CORS errors between frontend and backend typically indicate missing CORS middleware or incorrect origin configuration. Verify the backend CORS setup accepts requests from the frontend URL.
Installation Errors
Node.js version incompatibilities cause obscure errors. Verify the installed version matches application requirements:
node -v
MongoDB repository configuration issues on AlmaLinux 10 may require manual verification of repository files and GPG keys. NPM permission errors when installing global packages indicate incorrect directory permissions. Reconfigure NPM prefix as described in the Node.js installation section.
Runtime Errors
Express server errors often relate to missing dependencies or syntax errors. Check server logs:
npm run dev
Examine error messages carefully. React build failures typically stem from dependency conflicts. Delete node_modules and package-lock.json, then reinstall:
rm -rf node_modules package-lock.json
npm install
PM2 process crashes require log analysis:
pm2 logs mern-backend --lines 100
Identify error patterns and address underlying causes.
Performance Issues
Optimize MongoDB queries using indexes. Create indexes on frequently queried fields:
db.items.createIndex({ createdAt: -1 })
Reduce React bundle size by implementing code splitting and lazy loading:
const Component = lazy(() => import('./Component'));
Configure PM2 cluster mode for load balancing across CPU cores:
pm2 start server.js -i max
This spawns worker processes matching available CPUs.
Congratulation’s! You have successfully installed MERN. Thanks for using this tutorial for installing the MERN Stack on your AlmaLinux OS 10 system. For additional help or useful information, we recommend you check the official MERN website.