Scaling to a 3-Tier Architecture on AWS with NGINX, React, Node.js & MongoDB

1. Introduction
In Phase 1, we deployed everything on a single server:
NGINX
React Frontend
Node.js Backend
MongoDB
While this setup works for learning, it is not suitable for real-world production systems.
Problems with Single Server (Phase 1)
Single point of failure (server crash = full downtime)
No scalability
Poor performance under load
Not production-ready
2. Phase 2
We move to a real-world production architecture by splitting components into multiple servers.
Each server has one responsibility only
Each service is maintained in a separate repository
Architecture Overview
Security Design (Very Important)
Only NGINX is exposed to the internet
Internal services communicate via private IPs
Strict firewall rules using Security Groups
| Server | Role | Port | Access |
|---|---|---|---|
| NGINX | Entry point | 80 | Public |
| Frontend | React App | 3000 | NGINX only |
| Backend | Node API | 5000 | NGINX only |
| MongoDB | Database | 27017 | Backend only |
- This improves security and isolation
Step 1 : Launch 4 EC2 Instances
Configuration:
- Ubuntu 22.04
- t2.micro
Same VPC
Same key pair
Create 4 EC2 instances:
nginx-server
frontend-server
backend-server
mongo-server
- After launching all EC2 instances, you must configure inbound rules in Security Groups.
Step 2 : MongoDB Setup (Database Server)
1. Connect to MongoDB Server
ssh -i key.pem ubuntu@MONGO_PUBLIC_IP
- This connects you to your database server where MongoDB will run.
2. Install MongoDB
- This installs MongoDB on your server
# Install required tools
sudo apt update
sudo apt install -y gnupg curl
# Add MongoDB GPG key
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
# Add MongoDB repository
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
# Update and install MongoDB
sudo apt update
sudo apt install -y mongodb-org
3. Start MongoDB
sudo systemctl start mongod
sudo systemctl enable mongod
Start MongoDB
Enable auto-start after reboot
4. Allow Remote Access
Edit config:
sudo vi /etc/mongod.conf
Change:
bindIp: 127.0.0.1
To:
bindIp: 0.0.0.0
What does this mean?
| Value | Meaning |
|---|---|
127.0.0.1 |
Only local access (same server only) |
0.0.0.0 |
Allow connections from other servers |
Why we change this?
In our architecture: Backend server is different machine
MongoDB must accept external connection
So backend can connect like:
mongodb://MONGO_PRIVATE_IP:27017
5. Restart MongoDB
sudo systemctl restart mongod
6. Verify MongoDB
sudo systemctl statusmongod
You should see:
Active: active (running)
NGINX does NOT talk to MongoDB
Only backend connects to database
Step 3 : Backend Setup (Node.js Server)
1. Install Node.js
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
- Installs Node.js on your backend server
Adds Node.js repository
Installs Node.js + npm
Verify Installations:
node -v
npm -v
Shows Node.js version
Shows npm version
2. Install Git
sudo apt install git -y
- This installs Git on your server
3. Clone Project
git clone https://github.com/Niranjan-Projects/Backend.git
cd Backend
npm install
git clone→ copies project from GitHub
npm install→ installs all required packages
npm install
4. Create .env File
vi .env
Add This :
MONGO_URI=mongodb://<MONGO_PRIVATE_IP>:27017/devops
PORT=5000
JWT_SECRET=kkdevops
- Use
MongoDB PRIVATE IPonly (very important)
MONGO_URI→ connects backend to MongoDBJWT_SECRET→ used for login authenticationPORT→ backend runs on port 5000
5. Start Backend node server.js
node server.js
- Starts your backend server
Backend starts →
node server.jsruns your appConnects to MongoDB → using
.envconnection stringListens on port 5000 → ready to accept requests
Step 4 : Frontend Setup (React Server)
1. Install Git
sudo apt update
sudo apt install -y git
- Installs Git (needed to download code)
2. Install Node.js
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
Installs Node.js + npm
You saw warning (Node 18 deprecated) → it’s OK for now
3. Verify Installation
node -v
npm -v
- Confirms Node.js is installed
4. Clone Frontend Project
git clone https://github.com/Niranjan-Projects/Frontend.git
cd Frontend
- Downloads your React project
5. Install Dependencies
npm install
Installs all required packages
Warnings are normal → ignore
6. Create .env File
vi .env
Add:
REACT_APP_API_URL=/api
This is VERY IMPORTANT
What it means: Frontend will call /api NGINX will redirect it to backend
7. Install PM2
sudo npm install -g pm2
- Process manager (runs app in background)
8. Build Frontend (IMPORTANT )
npm run build
Creates:
build/
- Optimized production files
9. Install Serve
sudo npm install -g serve
- Tool to run production build
10. Run Frontend (Production Mode)
pm2 start "serve -s build -l 3000" --name frontend
- Runs app on port 3000
11. Save & Enable Auto Start
pm2 save
pm2 startup
- Keeps app running after reboot
12. Check Status
pm2 status
Expected: Online
Important Note
Currently, you can access your application using:
http://FRONTEND_PUBLIC_IP:3000
This is only for testing purposes
In real-world production: Users should NOT access port
3000Frontend should NOT be public
Now, we will configure NGINX to: Act as a public entry point
Route traffic to frontend
Route
/apirequests to backend
Step 5 : NGINX Setup (Final Integration)
In this step, we configure NGINX as the main entry point for our application.
Users will access only NGINX
NGINX will route traffic to frontend and backend
1. Install NGINX
sudo apt update
sudo apt install -y nginx
- Installs NGINX web server
2. Create NGINX Configuration
sudo vi /etc/nginx/sites-available/devops
Paste this configuration:
server {
listen 80;
server_name _;
# Frontend (React running on port 3000)
location / {
proxy_pass http://<FRONTEND_PRIVATE_IP>:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Backend (Node.js running on port 5000)
location /api/ {
proxy_pass http://<BACKEND_PRIVATE_IP>:5000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
<FRONTEND_PRIVATE_IP>→ Replace with your frontend server private IP<BACKEND_PRIVATE_IP>→ Replace with your backend server private IP
/ → goes to Frontend (React app)
/api/ → goes to Backend (Node.js API)
NGINX acts like a traffic manager
3. Enable Configuration
sudo ln -s /etc/nginx/sites-available/devops /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
Activates your new config
Removes default NGINX page
4. Test and Restart NGINX
sudo nginx -t
sudo systemctl restart nginx
- Make sure config is valid before restarting
Step 6 : Access Application via NGINX
Now your complete setup is ready
Open your browser and access:
http://NGINX_PUBLIC_IP
1. Open Application
The frontend will load successfully
This is served through NGINX (port 80)
2. Register a New User
Click on Student Login
Click Create Account
Enter details:
Name: Sai
Email: sai@gmail.com
Password: (your choice)
Click Signup
User will be created successfully
3. Login
Go back to login page
Enter same credentials
Click Login
You will be logged in successfully.
What is happening in background?
User → NGINX → Frontend
↓
/api
↓
Backend
↓
MongoDB
Frontend sends login request
NGINX forwards
/apito backendBackend validates and stores data in MongoDB
Step 6 : Connect to MongoDB & Verify Data
Now let’s check whether our application data is actually stored in MongoDB.
1. Connect to MongoDB Server
- This connects you to your MongoDB server
2. Open MongoDB Shell
mongosh
- This opens MongoDB CLI
3. Switch to Database
use devops
- Now you are inside your application database
4. Check Collections
show collections
Output:
users
- This means your app created a collection successfully
5. View User Data
db.users.find()
- You will see data like:
- User data stored correctly
Conclusion
In this phase, we successfully built a 3-tier architecture on AWS by separating frontend, backend, and database into different servers and using NGINX as the entry point.
This setup improves scalability, security, and reflects real-world production architecture.
Next
In the next blog, we will move forward by implementing Docker and Kubernetes to containerize and manage this application efficiently.
Docker → Containerizing applications
☸️ Kubernetes → Orchestrating and managing containers at scale



