Lab 04
Three-Tier Architecture
A multi-AZ network with public load balancing, private application instances, and a shared private PostgreSQL database.
VPC · ALB · EC2 · ASG · RDS · Nginx · Python
Lab completed
Problem
Build beyond a single public server.
The goal was to understand how a traditional web application is separated into network, application, and data layers—and how traffic, permissions, and availability change when the application spans two Availability Zones.
Instead of exposing EC2 or PostgreSQL directly to the internet, the architecture needed one controlled public entry point. The application instances would remain private, be managed as a group, and query the same private database.
Architecture
Public at the edge, private behind it.
VPC across two Availability Zones
Route tables determine each subnet’s path. The ALB accepts inbound web traffic; application and database resources remain in private subnets.
The internet-facing Application Load Balancer spans public subnets in two Availability Zones. Its target group routes healthy requests to EC2 instances launched from one template and managed by an Auto Scaling Group in private application subnets.
Both instances run Nginx in front of the Python application and connect to the same RDS PostgreSQL database in private database subnets. Security groups define which tier may initiate traffic to the next.
Build
Built the foundation before the application.
- 01Designed the VPC and subnets
Created public and private subnet groups across two Availability Zones so public entry points, application compute, and database resources could occupy different network layers.
- 02Configured internet and outbound routing
Attached an Internet Gateway for public-subnet connectivity, added a NAT Gateway for private application instances that need outbound access, and associated the appropriate route tables with each subnet.
- 03Separated traffic with security groups
Allowed web traffic to the ALB, application traffic from the ALB to EC2, and PostgreSQL traffic from the application tier to RDS. The EC2 and database tiers were not opened directly to the internet.
- 04Created the database tier
Provisioned private RDS PostgreSQL as the shared data service used by every application instance.
- 05Built the application runtime
Created a small Python database-backed application and placed Nginx in front of it on EC2. The
/messagepage queries PostgreSQL and prints the current EC2 hostname asServed by:. - 06Made instances repeatable
Captured the EC2 configuration in a Launch Template and used an Auto Scaling Group to maintain application instances across the two private subnets.
- 07Added load balancing and health checks
Created an internet-facing ALB, target group, listener, and health-check path so requests are routed only to registered healthy application instances.
What I Tested
The hostname made the invisible routing visible.
The application intentionally exposes one harmless diagnostic detail: the hostname of the EC2 instance serving the response. This made it possible to see the load balancer choose between otherwise identical targets.
- 01Target health
Verified both ASG-managed EC2 instances registered as healthy in the target group and could receive ALB traffic.
- 02Request distribution
Refreshed
/messageand watchedServed by:alternate between the two EC2 hostnames, demonstrating that the ALB was distributing requests. - 03Shared state
Confirmed that responses from either application instance queried the same RDS PostgreSQL database rather than storing data on an individual server.
- 04Network boundaries
Confirmed the public request enters through the ALB while the application instances and database remain on private addresses behind their security-group rules.
- 05Health-check path
Verified the target group used application health checks to determine whether each EC2 instance was eligible for new requests.
What I Learned
The hard part is how the layers connect.
- 01Public and private are routing decisions.
A resource’s subnet, route table, public address, and security-group rules work together. A “private tier” is not created by one checkbox.
- 02Security groups express service relationships.
The useful question is not simply which ports are open, but which tier is allowed to initiate a specific connection to which other tier.
- 03Load balancing depends on repeatable compute.
A Launch Template and Auto Scaling Group make the EC2 instances members of one application tier rather than individually configured servers.
- 04Shared data keeps the web tier interchangeable.
Because both instances use the same RDS database, the ALB can route a request to either server without splitting the application’s source of truth.
- 05Observability can start with a deliberate signal.
Displaying the serving hostname turned an abstract load-balancing claim into behavior I could repeatedly see and verify.
- 06Multi-AZ design affects every layer.
Subnets, ALB nodes, application targets, route associations, and database placement must be planned as one topology rather than as isolated resources.