Project: Scalable Web App Using AWS VPC, ALB, and Auto Scaling

Titus JamesTitus James
3 min read

In this project, I built a secure and scalable cloud infrastructure using AWS services. I set up a custom VPC with private and public subnets across two Availability Zones, used a bastion host for secure access, launched an Auto Scaling Group, and routed traffic using an Application Load Balancer (ALB).

Everything was set up using the AWS Management Console to clearly understand each step.


Architecture Diagram


Configuration Summary

  • 2 Availability Zones (for high availability)

  • 2 Public Subnets

  • 2 Private Subnets

  • 1 NAT Gateway per AZ

  • Auto Scaling Group in private subnets

  • Application Load Balancer (ALB) in public subnets


VPC Creation

From the AWS Console:

  • Chose “VPC and more” option

  • AWS automatically created:

    • VPC

    • 4 subnets (2 public, 2 private)

    • Route tables

    • Internet Gateway

    • 2 NAT Gateways

Note: Choosing “VPC only” would require setting up CIDRs, subnets, and gateways manually.


Auto Scaling Group Setup

Step 1: Create Launch Template

  • Go to EC2 → Launch Templates → Create

  • Name your template and describe it

  • Use a recent Ubuntu AMI

  • Create a new security group

    • Allow SSH (22) from anywhere

    • Allow TCP (8000) from anywhere

  • Select the VPC you just created

Launch the template

Step 2: Create Auto Scaling Group

  • Go to EC2 → Auto Scaling Groups → Create

  • Use the launch template

  • Select the same VPC

  • Choose the 2 private subnets

  • Set:

    • Desired Capacity: 2

    • Min: 1

    • Max: 4

  • Skip load balancer step (we’ll configure ALB separately)

  • No scaling policy needed

Step 3: Verify EC2 Instances

Check EC2 → Instances and confirm that 2 instances were launched inside the private subnets.


Bastion Host Setup

Private EC2s can't be accessed directly, so I created a bastion (jump) host:

  • Launched a new EC2 in public subnet

  • Allowed SSH (22) access

  • Auto-assigned a public IP

  • Transferred .pem file using scp:

$ scp -i my-key.pem my-key.pem ubuntu@<bastion-public-ip>:/home/ubuntu

From the bastion:

$ ssh -i my-key.pem ubuntu@<private-ec2-ip>

Application Deployment

  • Logged in via bastion

  • Installed Python dependencies

  • Ran the application on port 8000 on both instances


Application Load Balancer Setup

Step 1: Create ALB

  • Go to EC2 → Load Balancers → Create

  • Type: Application Load Balancer

  • Name: cloudscale-alb

  • Scheme: Internet-facing, IP type: IPv4

  • Select:

    • The same VPC

    • Both public subnets

  • Use a security group that allows port 80


Step 2: Create Target Group

  • Type: Instance

  • Protocol: HTTP

  • Port: 8000

  • Register the private EC2s

Attach this target group to the ALB listener (port 80)


Step 3: Update Security Groups

If the app doesn’t load at http://<ALB-DNS>:

  • Make sure port 80 is allowed in ALB’s security group

  • Ensure the target group is using HTTP, not TCP


Challenges I Faced

  • I mistakenly used TCP 8000 in the target group instead of HTTP 8000, which caused health checks to fail

  • Forgot to allow port 80 in the security group at first


Final Thoughts

This project helped me learn:

  • How to set up a production-grade AWS architecture

  • The role of VPC networking, subnets, NAT, and routing

  • How Auto Scaling and ALB improve availability and flexibility

  • Using a bastion host for secure SSH access

0
Subscribe to my newsletter

Read articles from Titus James directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Titus James
Titus James