Author: Eric Borba
Region: eu-central-1 (Europe — Frankfurt)
Deployment Date: May 2026
Live URL: http://project.ironhack-bootcamp-ericborba.work.gd
ALB DNS: web-alb-1083040517.eu-central-1.elb.amazonaws.com
A production-style 3-tier web application deployed on AWS: a public-facing Application Load Balancer (presentation tier), six Node.js EC2 instances managed by an Auto Scaling Group (asg-appserver-tier) spread across two Availability Zones (application tier), and two EC2-based data servers providing database connectivity (data tier) — all isolated inside a custom VPC with proper network segmentation, security group chaining, CloudWatch alarms, and a cost budget alert.
Internet
│
▼
[Application Load Balancer] — web-alb (internet-facing, HTTP:80)
│ in public-subnet-1a (10.0.1.0/24) — eu-central-1a
│ in public-subnet-1b (10.0.2.0/24) — eu-central-1b
│
├─── eu-central-1a ──────────────────────────────────────────┐
│ [app-server-1a-1] [app-server-1a-2] [app-server-1a-3] │
│ private-subnet-1a (10.0.11.0/24) │
└─── eu-central-1b ──────────────────────────────────────────┘
[app-server-1b-1] [app-server-1b-2] [app-server-1b-3]
private-subnet-1b (10.0.12.0/24)
│
▼
[Data Server 1a: 10.0.21.10] [Data Server 1b: 10.0.22.10]
data-subnet-1a (10.0.21.0/24) data-subnet-1b (10.0.22.0/24)
See ARCHITECTURE.md for full detail and architecture/ for diagrams.
ce-project-1-three-tier-architecture/
├── README.md ← this file
├── ARCHITECTURE.md ← detailed component docs
├── SECURITY.md ← security groups, IAM, isolation
├── COSTS.md ← monthly cost breakdown & optimizations
├── IMPROVEMENTS.md ← roadmap & production-readiness
├── architecture/
│ ├── architecture-diagram.svg ← full 3-tier overview
│ ├── network-diagram.svg ← VPC, subnets, routing
│ ├── security-groups-diagram.svg ← security boundaries
│ └── traffic-flow-diagram.svg ← request path through the stack
├── config/
│ ├── vpc-config.txt ← VPC and subnet details
│ ├── security-groups.txt ← all security group rules
│ ├── load-balancer-config.txt ← ALB and target group config
│ └── instances.txt ← EC2 instance details
├── app/
│ ├── server.js ← Node.js application
│ ├── package.json ← runtime manifest
│ └── deploy.sh ← bootstrap script for EC2
└── tests/
├── test-plan.md ← testing methodology
├── test-results.md ← test outcomes & evidence
└── failover-test.md ← HA / failover testing
- AWS CLI configured with appropriate IAM permissions
- An EC2 key pair in eu-central-1
- Node.js 18+ available on target instances
# Create the VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications \
'ResourceType=vpc,Tags=[{Key=Name,Value=bootcamp-vpc}]' \
--region eu-central-1
# Create subnets (repeat for each of the 6 subnets)
aws ec2 create-subnet --vpc-id <VPC_ID> \
--cidr-block 10.0.1.0/24 --availability-zone eu-central-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-subnet-1a}]'
# Attach Internet Gateway
aws ec2 create-internet-gateway --tag-specifications \
'ResourceType=internet-gateway,Tags=[{Key=Name,Value=bootcamp-igw}]'
aws ec2 attach-internet-gateway --vpc-id <VPC_ID> \
--internet-gateway-id <IGW_ID>
# Create NAT Gateways (one per AZ for HA)
aws ec2 create-nat-gateway --subnet-id <public-subnet-1a-id> \
--allocation-id <EIP_ALLOC_A> \
--tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=bootcamp-nat-gw}]'
aws ec2 create-nat-gateway --subnet-id <public-subnet-1b-id> \
--allocation-id <EIP_ALLOC_B> \
--tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=bootcamp-nat-gw-02}]'# ALB Security Group
aws ec2 create-security-group --group-name alb-sg \
--description "Security group for Application Load Balancer" \
--vpc-id <VPC_ID>
aws ec2 authorize-security-group-ingress --group-id <ALB_SG_ID> \
--protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id <ALB_SG_ID> \
--protocol tcp --port 443 --cidr 0.0.0.0/0
# Web Servers Security Group
aws ec2 create-security-group --group-name web-servers-sg \
--description "App tier — allows HTTP only from ALB" --vpc-id <VPC_ID>
aws ec2 authorize-security-group-ingress --group-id <WEB_SG_ID> \
--protocol tcp --port 80 --source-group <ALB_SG_ID>
# Data Tier Security Group
aws ec2 create-security-group --group-name data-tier-sg \
--description "Data tier — allows DB ports from app tier only" \
--vpc-id <VPC_ID>
aws ec2 authorize-security-group-ingress --group-id <DATA_SG_ID> \
--protocol tcp --port 3306 --source-group <WEB_SG_ID>
aws ec2 authorize-security-group-ingress --group-id <DATA_SG_ID> \
--protocol tcp --port 5432 --source-group <WEB_SG_ID>Launch 3 instances in each private subnet (t3.micro, Amazon Linux 2), then on each:
# Copy the bootstrap script and run it
scp -i <KEY>.pem app/deploy.sh ec2-user@<PRIVATE_IP>:~
ssh -i <KEY>.pem ec2-user@<PRIVATE_IP> "sudo bash ~/deploy.sh"# Create Target Group
aws elbv2 create-target-group --name web-servers-tg \
--protocol HTTP --port 80 --vpc-id <VPC_ID> \
--health-check-path /health --health-check-interval-seconds 10 \
--healthy-threshold-count 2 --unhealthy-threshold-count 2
# Create ALB
aws elbv2 create-load-balancer --name web-alb --type application \
--scheme internet-facing --ip-address-type ipv4 \
--subnets <PUBLIC_SUBNET_1A> <PUBLIC_SUBNET_1B> \
--security-groups <ALB_SG_ID>
# Register all 6 instances with the target group
aws elbv2 register-targets --target-group-arn <TG_ARN> \
--targets Id=<INSTANCE_1> Id=<INSTANCE_2> ...
# Add HTTP listener
aws elbv2 create-listener --load-balancer-arn <ALB_ARN> \
--protocol HTTP --port 80 \
--default-actions Type=forward,TargetGroupArn=<TG_ARN># Test load balancer responds
curl -s http://web-alb-1083040517.eu-central-1.elb.amazonaws.com/health
# Expected: {"status":"healthy","instance":"i-xxx","az":"eu-central-1x","timestamp":"..."}
# Hit the main page multiple times to observe different instance IDs
for i in {1..10}; do
curl -s http://web-alb-1083040517.eu-central-1.elb.amazonaws.com/ \
| grep -oP 'eu-central-1[ab]'
done# Direct access to app tier should fail (no public IP / SG blocks it)
curl -s --max-time 3 http://10.0.11.7/ # Times out — correct
curl -s --max-time 3 http://10.0.21.10/ # Times out — correctcurl http://web-alb-1083040517.eu-central-1.elb.amazonaws.com/healthSee tests/test-plan.md and tests/test-results.md for full results.
| Name | Role |
|---|---|
| Eric Borba | Cloud Engineer (solo project) |