This project implements a secure and scalable 3-tier architecture on AWS. The presentation layer uses an Application Load Balancer deployed in public subnets. The application layer consists of Spring Boot applications running on EC2 instances in private subnets and scaled via Auto Scaling. The database layer uses Amazon RDS deployed in private subnets. A Bastion Host provides secure administrative access, and a NAT Gateway enables outbound internet access for private instances.
This article walks through everything I built, step by step, and explains why each component exists.
What We’re Building
At the end of this project, the application looks like this:
User
↓
Route 53
↓
CloudFront
↓
Application Load Balancer
↓
EC2 Auto Scaling Group
↓ ↓
Redis Cache RDS Proxy
↓
RDS MySQL + Read Replica
The goal wasn’t just to “make it work”, but to make it:
Secure
Scalable
Highly available
Production-ready
PHASE 1: NETWORKING (FOUNDATION)
- Create VPC
-
Create Subnets (8 total)
- Public Subnets (2 total)
- Used for Load Balancer, NAT Gateway, Bastion Host
- Private App Subnets (2 total)
- Used for EC2 Auto Scaling Group
- Private DB Subnets (2 total)
- Used for RDS, RDS Proxy, Read Replica
- Private Cache Subnets (2 total)
- Used for Redis (ElastiCache)
- Public Subnets (2 total)
-
Internet Gateway - Allows public subnets to access the internet.
- Create Internet Gateway
- Attach Internet Gateway to VPC
-
NAT Gateway - Private subnets need outbound-only internet access.
- Create a NAT Gateway in public subnet and allocate an elastic IP.
-
Route Tables
- Public Route Table
- Route: '0.0.0.0/0 → Internet Gateway'
- Subnet Associate: 'public-a', 'public-b'
- Private Route Table
- Route: '0.0.0.0/0' → 'NAT Gateway'
- Subnet Associate: 'private-app-', 'private-db-', 'private-cache-*'
- Public Route Table
-
Security Groups
- ALB Security Group (alb-sg)
- Inbound: '80, 443' from '0.0.0.0/0'
- Outbound: 8080 to 'app-sg'
- Bastion Security Group (bastion-sg)
- Inbound: 'SSH (22)' 'from your IP'
- Outbound: 'SSH' to 'app-sg'
- App Security Group (app-sg)
- Inbound: '8080' from 'alb-sg', '22' from 'bastion-sg'
- Outbound: 'All'
- DB Security Group (db-sg)
- Inbound: '3306' from 'app-sg'
- Outbound: 'Local'
- Redis Security Group (redis-sg)
- Inbound: '6379' from 'app-sg'
- Outbound: 'Local'
- ALB Security Group (alb-sg)
PHASE 2: COMPUTE LAYER
-
Create a Bastion Host - This is the only SSH entry point.
- Subnet: public-a
- Public IP: Enabled
- SG: bastion-sg
-
Create an Application Load Balancer
- Type: Application
- Scheme: Internet-facing
- Subnets: public-a, public-b
- SG: alb-sg
-
Create a Target Group - ALB routes traffic to target groups, not directly to EC2.
- Target type: Instance
- Port: 8080
- Health check path: /health
-
Create a Launch Template
- AMI: Ubuntu
- Instance type: t3.micro
- SG: app-sg
- IAM Role: ec2-app-role
- User data: {}
-
Create an Auto Scaling Group
- Subnets: private-app-a, private-app-b
- Min: 1 | Desired: 2 | Max: 3
- Attach ALB target group
PHASE 3: DATABASE LAYER
- Create DB Subnet Group (RDS must know which private subnets to use) and select private DB subnets.
- Create RDS MySQL database
- Create AWS Secret Manager for DB password and enable autamatic rotation.
- Create Read Replica
- Create RDS Proxy
PHASE 4: REDIS CACHE
- Create Cache Subnet Group and select private cache subnets
- Create Redis Cluster
PHASE 5: Cloudfront & Route53
- Request SSL Certificate
- Create CloudFront Distribution
- Create a hosted zone record
Source Code Link
To run the application:
git clone https://github.com/dipakprasad22/AWS-3tier-Java-Spring-Boot-App.git
mvn clean package
java -Xms256m -Xmx512m -jar app.jar
curl http://localhost:8080/health
http://<ALB-DNS-NAME>/hello


Top comments (0)