
Building a 3-Tier Architecture on AWS: A Practical End-to-End Guide
Learn how to design and deploy a secure, highly available 3-tier application using Amazon VPC, Application Load Balancer, EC2 Auto Scaling, private subnets, NAT Gateway, and a managed database.
Introduction
A common mistake when learning AWS architecture is to think of an application as simply:
User → EC2 → Database
That may work for a small experiment, but production applications usually need stronger separation between the public entry point, application workloads, and data.
A 3-tier architecture separates an application into three logical layers:
- Presentation Tier — receives user requests.
- Application Tier — processes business logic.
- Data Tier — stores application data.
A typical AWS implementation looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
INTERNET
|
v
+-------------------+
| Application |
| Load Balancer |
| Public Subnets |
+---------+---------+
|
v
+-------------------+
| Application Tier |
| Private Subnets |
| EC2 + Auto Scaling|
+---------+---------+
|
v
+-------------------+
| Data Tier |
| Private Subnets |
| Amazon RDS |
+-------------------+The important principle is:
Only the components that need to be public should be publicly reachable.
1. Understanding the Three Tiers
Tier 1 — Presentation Tier
This is the entry point for users.
Examples include:
- Application Load Balancer
- Web servers
- Frontend applications
In our architecture, the Application Load Balancer (ALB) is internet-facing.
Users communicate with the ALB instead of directly connecting to application EC2 instances.
1
2
3
4
5
User
|
| HTTPS
v
ALBTier 2 — Application Tier
This layer contains the application's business logic.
For example:
- REST API
- Backend service
- Authentication logic
- Business processing
We can run the application on EC2 instances inside private subnets.
1
2
3
4
5
6
7
ALB
|
v
Private EC2
|
v
Backend ApplicationBecause these instances don't need to accept direct internet traffic, there is no reason to expose them with public IP addresses.
Tier 3 — Data Tier
This layer stores application data.
For this project, we can use:
Amazon RDS
Examples include:
- PostgreSQL
- MySQL
- MariaDB
The database should remain inside private subnets.
1
2
3
4
Application EC2
|
v
RDSThe database security group should allow database traffic only from the application tier.
2. Complete Architecture
A more production-oriented design could look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
INTERNET
|
v
+---------------------------+
| Application Load Balancer |
| Public Subnet |
| AZ-1 |
+-------------+-------------+
|
|
+-------------+-------------+
| |
v v
+-------------+ +-------------+
| Application | | Application |
| EC2 | | EC2 |
| Private AZ1 | | Private AZ2 |
+------+------+ +------+------+
| |
+-------------+-------------+
|
v
+---------------+
| Amazon RDS |
| Private Subnet|
+---------------+
Private EC2
|
v
NAT Gateway
|
v
Internet Gateway
|
v
InternetFor high availability, the ALB and application tier should span multiple Availability Zones.
3. Step 1 — Design the VPC
Before creating resources, design the network.
Let's use:
1
2
VPC CIDR:
10.0.0.0/16We can divide it into four subnets:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Availability Zone 1
Public Subnet
10.0.1.0/24
Private App Subnet
10.0.11.0/24
Availability Zone 2
Public Subnet
10.0.2.0/24
Private App Subnet
10.0.12.0/24For the database, we should use separate private subnets:
1
2
3
4
5
Private DB Subnet AZ-1
10.0.21.0/24
Private DB Subnet AZ-2
10.0.22.0/24Our logical structure becomes:
1
2
3
4
5
6
7
8
9
10
11
VPC
│
├── AZ-1
│ ├── Public Subnet
│ ├── Private App Subnet
│ └── Private DB Subnet
│
└── AZ-2
├── Public Subnet
├── Private App Subnet
└── Private DB SubnetEach subnet belongs to one Availability Zone.
4. Why Multiple Availability Zones?
Suppose everything is deployed in one Availability Zone:
1
2
3
4
5
AZ-1
|
+-- ALB
+-- EC2
+-- DatabaseIf that AZ experiences a failure, the entire application can be affected.
Instead:
1
2
3
4
5
6
7
AZ-1 AZ-2
EC2 EC2
| |
+----------+-----------+
|
ALBSpreading workloads across multiple AZs reduces dependence on a single Availability Zone.
This is one of the foundations of highly available AWS architectures.
5. Step 2 — Create the Internet Gateway
Attach an Internet Gateway to the VPC.
The public route table can contain:
1
2
3
4
Destination Target
--------------------------------
10.0.0.0/16 local
0.0.0.0/0 Internet GatewayThis allows resources in the public subnet to communicate with the internet when their security configuration permits it.
The ALB will use these public subnets.
6. Step 3 — Create NAT Gateway
Our application servers are private.
However, they might need outbound internet access for:
- Operating system updates
- Installing packages
- Downloading dependencies
- Calling approved external APIs
We therefore create a NAT Gateway in a public subnet.
Traffic becomes:
1
2
3
4
5
6
7
8
9
10
11
12
13
Private EC2
|
v
Private Route Table
|
v
NAT Gateway
|
v
Internet Gateway
|
v
InternetThe private application route table can contain:
1
2
3
4
Destination Target
--------------------------------
10.0.0.0/16 local
0.0.0.0/0 NAT GatewayThe key point is:
NAT provides outbound internet access; it does not make the private EC2 instance publicly accessible.
For production environments, NAT Gateway placement and cost should be evaluated carefully, especially when workloads span multiple AZs.
7. Step 4 — Configure Security Groups
This is one of the most important parts of the architecture.
We should create separate security groups for each tier.
ALB Security Group
Allow:
1
2
3
Inbound:
HTTPS 443 → 0.0.0.0/0For a temporary learning setup, HTTP 80 can also be used.
Application Security Group
Allow application traffic only from the ALB security group.
For example:
1
2
HTTP 80
Source: ALB Security GroupNot:
1
2
HTTP 80
Source: 0.0.0.0/0Database Security Group
Suppose RDS PostgreSQL uses port 5432.
Allow:
1
2
TCP 5432
Source: Application Security GroupThis creates a security chain:
1
2
3
4
5
6
7
8
9
10
Internet
|
v
ALB SG
|
v
Application SG
|
v
Database SGEach layer trusts only the layer that should communicate with it.
8. Step 5 — Create the Application Load Balancer
Create:
Application Load Balancer
Configuration:
1
2
3
4
5
6
7
8
9
10
11
12
Scheme:
Internet-facing
IP Address Type:
IPv4
VPC:
Application VPC
Subnets:
Public AZ-1
Public AZ-2The ALB becomes the public entry point.
1
2
3
4
Internet
|
v
ALBUsers should not need to know the private IP addresses of application servers.
9. Step 6 — Create the Target Group
Create a target group for the application EC2 instances.
Example:
1
2
3
4
5
6
7
8
9
10
11
Target Type:
Instances
Protocol:
HTTP
Port:
80
Health Check:
HTTP /The target group might contain:
1
2
EC2-1 → Healthy
EC2-2 → HealthyIf an instance fails its health checks, the ALB can stop routing normal traffic to that unhealthy target.
10. Step 7 — Create the EC2 Launch Template
Instead of manually configuring every EC2 instance, create a Launch Template.
The template can define:
- AMI
- Instance type
- Security group
- IAM role
- EBS volume
- User Data
- Tags
For example:
1
2
3
4
5
6
7
8
Launch Template
|
+-- Ubuntu
+-- t3.micro
+-- Application SG
+-- IAM Role
+-- EBS
+-- User DataThis becomes the blueprint for application servers.
11. Step 8 — Configure User Data
We can automatically install the application when an EC2 instance launches.
For example:
1
2
3
4
5
6
7
8
9
10
apt-get update -y
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
echo "<h1>3-Tier AWS Application</h1>" \
> /var/www/html/index.htmlNow every EC2 instance launched from the template can automatically configure itself.
This becomes especially useful when Auto Scaling creates additional instances.
12. Step 9 — Create the Auto Scaling Group
Create an Auto Scaling Group using the Launch Template.
Example:
1
2
3
4
5
6
7
8
Minimum:
2
Desired:
2
Maximum:
6Select the private application subnets:
1
2
Private App AZ-1
Private App AZ-2Now our application tier can scale horizontally.
1
2
3
4
5
6
ALB
|
+-------+-------+
| |
v v
EC2-1 EC2-2If traffic increases:
1
2
3
4
ALB
/ | \
v v v
EC2-1 EC2-2 EC2-313. Step 10 — Configure Scaling Policies
Auto Scaling isn't useful simply because it can create instances.
We need to tell it when to scale.
For example:
1
2
3
4
Average CPU > 60%
|
v
Scale OutAnd:
1
2
3
4
Average CPU < 30%
|
v
Scale InIn a production system, CPU may not be the best metric.
Depending on the application, useful metrics can include:
- ALB request count per target
- CPU utilization
- Memory utilization
- Queue depth
- Application-specific CloudWatch metrics
The scaling metric should represent actual application demand.
14. Step 11 — Create the Database Subnets
Now create a separate subnet group for RDS.
Example:
1
2
3
4
5
DB Subnet AZ-1
10.0.21.0/24
DB Subnet AZ-2
10.0.22.0/24These should be private subnets.
Create an RDS DB Subnet Group using both database subnets.
15. Step 12 — Create Amazon RDS
For the learning project, we can use:
1
2
3
4
5
6
7
8
9
10
11
Engine:
PostgreSQL
Deployment:
Multi-AZ where appropriate
Subnet:
Private DB Subnet Group
Public Access:
NoThe application tier connects to RDS using the database endpoint.
1
2
3
4
5
Application EC2
|
| TCP 5432
v
Amazon RDSThe database should not be directly accessible from the internet.
16. How the Complete Request Works
Let's follow one request.
A user opens:
1
https://example.comStep 1
DNS resolves the application endpoint.
Step 2
The request reaches the public ALB.
1
2
3
4
Internet
|
v
ALBStep 3
The ALB checks the target group.
1
2
EC2-1 → Healthy
EC2-2 → HealthyStep 4
The ALB selects a healthy application server.
1
ALB → EC2-1Step 5
The application processes the request.
If data is required:
1
2
3
4
EC2
|
v
RDSStep 6
The response travels back:
1
2
3
4
5
6
7
8
9
10
RDS
|
v
EC2
|
v
ALB
|
v
User17. Frontend and Backend in a 3-Tier Design
There are actually two ways people commonly describe "3-tier."
Traditional application model
1
2
3
4
5
Presentation
|
Application
|
DataAWS implementation
1
2
3
4
5
ALB/Web
|
EC2/Application
|
RDSFor a larger application, we can further separate frontend and backend services:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Internet
|
v
ALB
|
v
Frontend Service
|
v
Internal Load Balancer
|
v
Backend Service
|
v
RDSThis can provide stronger separation and independent scaling.
18. Why Use an Internal Load Balancer?
Suppose we have multiple backend instances:
1
2
3
4
5
6
7
Frontend
|
v
Internal ALB
/ \
v v
API-1 API-2The backend services don't need to be publicly accessible.
The internal ALB provides a private endpoint inside the VPC.
This is useful for microservices and service-to-service communication.
19. VPC Routing
Understanding routing is essential.
The VPC automatically provides a local route for traffic within the VPC CIDR.
For example:
1
2
3
Destination Target
--------------------------------
10.0.0.0/16 localPublic subnet:
1
2
3
0.0.0.0/0
↓
Internet GatewayPrivate application subnet:
1
2
3
0.0.0.0/0
↓
NAT GatewayDatabase subnet:
1
No direct internet routeThis creates different network boundaries for each tier.
20. Why Should the Database Be Private?
A database usually doesn't need to communicate directly with internet users.
Imagine:
1
2
3
4
Internet
|
X
DatabaseInstead:
1
2
3
4
5
6
7
8
9
10
Internet
|
v
ALB
|
v
Application
|
v
DatabaseIf the database is not directly exposed, its attack surface is significantly reduced.
The database security group should accept connections only from the application tier.
21. High Availability
Our architecture can survive several types of failures better than a single-server deployment.
For example:
1
2
3
4
5
6
7
AZ-1 AZ-2
EC2-1 EC2-2
| |
+-------- ALB -------+
|
UsersIf one application instance fails, another healthy instance can continue serving requests.
If Auto Scaling is configured correctly, the failed capacity can also be replaced.
For the database layer, RDS Multi-AZ can provide a standby database in another Availability Zone for failover.
22. Monitoring the Architecture
A production architecture needs visibility.
Useful AWS services include:
Amazon CloudWatch
Monitor:
- EC2 CPU
- ALB request count
- ALB response time
- Target health
- Auto Scaling activity
- RDS metrics
VPC Flow Logs
Useful for investigating network traffic.
ALB Access Logs
Useful for analyzing HTTP requests.
CloudTrail
Useful for auditing AWS API activity.
A useful production mindset is:
If something fails at 2 AM, can I understand what happened without logging into every server manually?
23. Security Improvements for Production
The basic architecture is a strong learning foundation, but production requires additional controls.
I would consider:
AWS WAF
Protect the public ALB against common web attacks.
HTTPS
Use TLS certificates through AWS Certificate Manager.
IAM Roles
Avoid storing AWS credentials inside EC2.
Secrets Manager
Store database passwords and application secrets securely.
Systems Manager
Use Session Manager where appropriate instead of distributing SSH keys.
VPC Endpoints
Use private connectivity to supported AWS services where appropriate, potentially reducing NAT dependency and improving network security.
Encryption
Use encryption for:
- EBS
- RDS
- S3
- Application traffic
- Secrets
24. Cost Considerations
A highly available architecture costs more than a single EC2 instance.
Potential cost components include:
- EC2
- Application Load Balancer
- NAT Gateway
- RDS
- EBS
- CloudWatch
- Data transfer
NAT Gateway deserves special attention because it has both hourly and data-processing charges.
For workloads that heavily access AWS services such as S3, consider whether appropriate VPC endpoints can reduce unnecessary NAT processing and data-transfer paths.
Architecture decisions should therefore consider:
Security + Availability + Performance + Cost
—not just whether the application works.
25. Common Beginner Mistakes
Mistake 1 — Putting EC2 directly on the internet
Better:
1
Internet → ALB → Private EC2Mistake 2 — Making RDS publicly accessible
Prefer:
1
Private EC2 → Private RDSMistake 3 — Allowing 0.0.0.0/0 everywhere
Use security-group-to-security-group rules wherever possible.
Mistake 4 — Running everything in one AZ
Use multiple AZs for workloads that require high availability.
Mistake 5 — Manually configuring every EC2
Use Launch Templates and Auto Scaling.
Mistake 6 — Assuming "private" means "no internet access"
Private instances can have controlled outbound connectivity through NAT Gateway or VPC endpoints.
Mistake 7 — Ignoring data-transfer costs
Cross-AZ and NAT traffic can become significant at scale.
26. Testing the Architecture
After deployment, don't just check whether the website opens.
Test each layer.
Test 1 — ALB
1
curl http://<ALB-DNS>Test 2 — Target Health
Verify that targets show:
1
HealthyTest 3 — Scaling
Generate controlled traffic and observe Auto Scaling.
Test 4 — Failure
Terminate one application instance and observe whether:
- ALB removes it from service
- Auto Scaling launches a replacement
- The application remains available
Test 5 — Database Connectivity
Verify that the application can connect to RDS.
Test 6 — Security
Confirm that:
1
Internet → EC2is not directly possible.
Also verify that:
1
Internet → RDSis not possible.
27. Recommended Build Order
When building this project yourself, don't randomly create resources.
Follow this sequence:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1. Plan CIDR
↓
2. Create VPC
↓
3. Create Subnets
↓
4. Create Internet Gateway
↓
5. Create Route Tables
↓
6. Create NAT Gateway
↓
7. Create Security Groups
↓
8. Create RDS Subnet Group
↓
9. Create RDS
↓
10. Create Launch Template
↓
11. Create Target Group
↓
12. Create Application Load Balancer
↓
13. Create Auto Scaling Group
↓
14. Configure Scaling
↓
15. Configure DNS/HTTPS
↓
16. Test
↓
17. MonitorFollowing the dependency order makes troubleshooting much easier.
28. Turning This into a GitHub Project
For a portfolio project, I would not stop at creating everything manually in the AWS Console.
A better next step is Infrastructure as Code.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
aws-3-tier-architecture/
│
├── README.md
│
├── architecture/
│ └── architecture.png
│
├── terraform/
│ ├── provider.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── vpc.tf
│ ├── subnets.tf
│ ├── routes.tf
│ ├── nat.tf
│ ├── security-groups.tf
│ ├── alb.tf
│ ├── target-group.tf
│ ├── launch-template.tf
│ ├── autoscaling.tf
│ └── rds.tf
│
├── application/
│ ├── frontend/
│ └── backend/
│
└── scripts/
└── user-data.shThen the entire environment can be recreated consistently.
29. Final Architecture
Putting everything together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
INTERNET
|
v
Route 53 / DNS
|
v
+----------------------+
| Application LB |
| Public Subnets |
+----------+-----------+
|
+----------+----------+
| |
v v
+-------------+ +-------------+
| Application | | Application |
| EC2 / ASG | | EC2 / ASG |
| Private AZ1 | | Private AZ2 |
+------+------+ +------+------+
| |
+----------+----------+
|
v
+----------------------+
| Amazon RDS |
| Private Subnets |
| Multi-AZ |
+----------------------+
Private Application EC2
|
v
NAT Gateway
|
v
Internet Gateway
|
v
InternetThe security boundaries look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
Internet
|
| HTTPS
v
ALB Security Group
|
| Application Port
v
Application Security Group
|
| DB Port
v
Database Security GroupConclusion
Building a 3-tier architecture is not simply about launching an EC2 instance and connecting a database.
It is about understanding how the application should communicate, where each component should live, who is allowed to communicate with whom, and how the system behaves when traffic or failures occur.
The core architecture is:
Internet → ALB → Private Application Tier → Private Database Tier
And the supporting AWS services provide:
- VPC → network isolation
- Subnets → resource placement
- Route Tables → traffic direction
- Internet Gateway → public internet connectivity
- NAT Gateway → controlled outbound connectivity
- Security Groups → stateful network access control
- ALB → traffic distribution
- Launch Template → consistent EC2 configuration
- Auto Scaling → elasticity and recovery
- RDS → managed relational database
- CloudWatch → monitoring and observability
The biggest lesson is:
A production-oriented AWS architecture is designed around boundaries: network boundaries, security boundaries, failure boundaries, and scaling boundaries.
Once these concepts become clear, AWS services stop feeling like isolated products and start becoming building blocks for complete solutions.
This is also the direction I'm taking in my AWS Solutions Architect learning journey: not just learning how to create a service, but understanding why it belongs in an architecture and what happens when the workload grows or something fails. 🚀☁️
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article