DEV Community

Shraddha
Shraddha

Posted on

Three Tier application on AWS.

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:

  1. Presentation Layer (Frontend)
  2. Application Layer (Backend)
  3. Data Layer (Database)

P.S.This post is a part of DMI Cohort run by Pravin Mishra.

Top comments (0)