While we deploy a three tier application,its very important to understand the three tiers or layers in short.I have explained the layers in brief:
Tier 1 — Presentation Layer (Frontend)
Component: Nginx on EC2
Subnet: Public subnet
Port: 80 (HTTP)
- Role
- Serves static files (HTML, CSS, JS)
- Receives user requests
- Forwards API calls to backend
Why in public subnet?
Any application has a frontend serving the clients so needs internet access.So that the users connect directly to it.
Tier 2 — Application Layer (Backend)
Component: Node.js app (Express)
Runs on: Same EC2 instance
Port: 3000
Managed by: PM2
- Role
- Handles business logic
- Processes requests
- Communicates with database
**
Security**
Not directly exposed to internet.This layer is not exposed to the internet and no traffic is directly allowed as its a secured layer where userdata will reside.The backend is only accessible through Nginx.(webserver)
Tier 3 — Data Layer (Database)
Component: RDS MySQL
Subnet: Private subnet
Public access: Disabled
- Role
- Stores books, users, reviews
Here we have used RDS(Relational database service) of AWS.Only backend can connect so that its a highly secured application server.
- Security benefit
- Database not exposed to internet
- Reduces attack surface
The deployment of the three tier architecture goes like this:
NETWORK ARCHITECTURE :In this I have used VPC (virtual private cloud)
🔹 1️⃣ VPC
Name: capstone-vpc
- CIDR: 10.0.0.0/16
- DNS Resolution: ✅ Enabled
- DNS Hostnames: ✅ Enabled
🔹 2️⃣ Subnets (2 AZ High Availability)
- Public Subnets (Web + Public ALB + NAT)
- Name
- AZ
- CIDR
- web-public-a
- ap-south-1a
- 10.0.1.0/24
- web-public-b
- ap-south-1b
- 10.0.2.0/24
✅ Enable Auto-assign Public IP on both public subnets
- App Private Subnets (Internal ALB + App EC2)
- Name
- AZ
- CIDR
- app-private-a
- ap-south-1a
- 10.0.3.0/24
- app-private-b
- ap-south-1b
10.0.4.0/24
DB Private Subnets (RDS only)
Name
AZ
CIDR
db-private-a
ap-south-1a
10.0.5.0/24
db-private-b
ap-south-1b
10.0.6.0/24
🔹 3️⃣ Internet Gateway
Name: capstone-igw
Attach to capstone-vpc
🔹 4️⃣ NAT Gateway
Create in:
- Subnet: web-public-a
- Name: capstone-nat
- Connectivity: Public
- Allocate Elastic IP
- Wait until status = Available
🔹 5️⃣ Route Tables
Public Route Table
- Name: capstone-public-rt
- Route:
- 0.0.0.0/0 → capstone-igw
- Associate:
- web-public-a
- web-public-b
Private Route Table
Name: capstone-private-rt
Route:
0.0.0.0/0 → capstone-nat
Associate:
app-private-a
app-private-b
db-private-a
db-private-b
✔ Network design is now correct.
✅ PART 2 — SECURITY GROUPS Chain:
Internet → Public ALB → Web EC2 → Internal ALB → App EC2 → RDS
1️⃣ Public ALB SG
Name: capstone-public-alb-sg
Inbound:
HTTP 80 → 0.0.0.0/0
Outbound:
Allow all
2️⃣ Web EC2 SG
Name: capstone-web-ec2-sg
Inbound:
HTTP 80 → Source: capstone-public-alb-sg
SSH 22 → My IP
Outbound:
Allow all
3️⃣ Internal ALB SG
Name: capstone-internal-alb-sg
Inbound:
HTTP 80 → Source: capstone-web-ec2-sg
Outbound:
Allow all
⚠ Not 0.0.0.0/0
4️⃣ App EC2 SG
Name: capstone-app-ec2-sg
Inbound:
Custom TCP 3001 → Source: capstone-internal-alb-sg
SSH 22 → Source: capstone-web-ec2-sg
Outbound:
Allow all
5️⃣ DB SG
Name: capstone-db-sg
Inbound:
MySQL 3306 → Source: capstone-app-ec2-sg
Outbound:
Default
Security design is correct ✅
✅ TASK 3 — RDS (Highly Available)
Engine: MySQL
Multi-AZ: Enabled
Read Replica: Enabled
Public Access: ❌ No
Subnet Group: db-private-a & db-private-b
Security Group: capstone-db-sg
Initial DB Name: databasename
✅ EC2 INSTANCES
AMI: Ubuntu Server 22.04 LTS
Username:
ubuntu
web-ec2
Subnet: web-public-a
Auto Public IP: Enabled
SG: capstone-web-ec2-sg
Key: capstone-key.pem
app-ec2
Subnet: app-private-a
Public IP: Disabled
SG: capstone-app-ec2-sg
✅ INTERNAL TARGET GROUP
Name: capstone-app-tg
Protocol: HTTP
Port: 3001
VPC: capstone-vpc
Target: app-ec2
Health Check:
Path: /
Port: traffic port
✅ INTERNAL ALB
Name: capstone-internal-alb
Scheme: Internal
- Subnets:
- app-private-a
- app-private-b
Security Group:
capstone-internal-alb-sg
Listener:
HTTP 80 → Forward to capstone-app-tg
✅ SSH (UBUNTU)
From local machine:
chmod 400 capstone-key.pem
ssh -i capstone-key.pem ubuntu@
From web to app:
ssh -i capstone-key.pem ubuntu@
(You may use SSH agent forwarding instead — better practice)
✅ BACKEND SETUP (UBUNTU)
On app-ec2:
sudo apt update && sudo apt upgrade -y
sudo apt install git nodejs npm mysql-client -y
sudo npm install -g pm2
Clone:
git clone https://github.com/pravinmishraaws/book-review-app
cd book-review-app
npm install
cd backend
Edit:
nano .env
Add:
DB_HOST=
DB_USER=admin
DB_PASSWORD=Password123!
DB_NAME=databasename
PORT=3001
ALLOWED_ORIGIN=http://
Start:
pm2 start src/server.js --name backend
pm2 startup
pm2 save
✅ FRONTEND SETUP (UBUNTU)
On web-ec2:
sudo apt update
sudo apt install nginx git nodejs npm -y
sudo npm install -g npm
sudo npm install -g pm2
Clone:
git clone https://github.com/application_repo
cd app/frontend
npm install
Create .env:
NEXT_PUBLIC_API_URL=/api
Build:
npm run build
pm2 start npm --name frontend -- start
pm2 save
✅ NGINX CONFIG
Edit:
sudo nano /etc/nginx/sites-available/default
Use your internal ALB DNS:
proxy_pass http://;
Test:
sudo nginx -t
sudo systemctl restart nginx
✅ PUBLIC ALB
Name: capstone-public-alb
Scheme: Internet-facing
- Subnets:
- web-public-a
- web-public-b
Security Group:
capstone-public-alb-sg
Target Group:
- capstone-web-tg
- Port 80
- Target: web-ec2
✅ FINAL TRAFFIC FLOW
User
↓
Public ALB
↓
Web EC2 (Ubuntu + Nginx + Frontend)
↓
Internal ALB
↓
App EC2 (Ubuntu + PM2 Backend 3001)
↓
RDS (Multi-AZ MySQL)
In this designed architecture, we can host the application in three tiers:
- Presentation Layer (Frontend)
- Application Layer (Backend)
- Data Layer (Database)
P.S.This post is a part of DMI Cohort run by Pravin Mishra.
Top comments (0)