Learning cloud networking by building a multi-AZ VPC from scratch
CIDR subnetting, NAT routing, ALB target groups, and least-privilege IAM policies.
The Trigger & Underlying Problem
The Trigger: I wanted to understand what actually happens when packets travel across cloud infrastructure instead of clicking through AWS default VPC wizards.
The Core Problem: I wanted to see a VPC with public and private subnets in two availability zones, instead of leaving everything in a default VPC. On the Expense Tracker diagram the frontend instances sit in the public subnets. The backend instances sit in the private app subnets.
Why Standard Solutions Were Insufficient: This was a deliberate learning lab designed to master subnet arithmetic, routing tables, and security group behaviors.
What It Solves in Practice
A personal lab network: public subnets for the load balancer, NAT gateway, and frontend instances, and private subnets for the backend instances.
Provides a clean template for deploying microservices, APIs, and databases securely on AWS.
Instances were launched in default VPCs with all ports open to 0.0.0.0/0.
Architecture & Key Engineering Decisions
Created a custom 10.0.0.0/16 VPC spanning two Availability Zones in ap-south-1. Divided into two public subnets (/24) and two private subnets (/24).
- Attached an Internet Gateway (IGW) to public subnets and provisioned a NAT Gateway with an Elastic IP in a public subnet for outbound-only private egress.
- Separated route tables: the public route table routes 0.0.0.0/0 to the IGW; the private route table routes 0.0.0.0/0 to the NAT Gateway.
- The architecture diagram draws an application load balancer across the public subnets, with frontend EC2 in those public subnets and backend EC2 in the private app subnets.
What Failed in Practice & How It Was Addressed
- EC2 instances in private subnets were unable to run `apt-get update` or reach GitHub because I forgot to associate the private subnet with the custom private route table.
- Security group misconfiguration: ALB health checks returned 502 Bad Gateway because the EC2 security group only allowed traffic from my personal IP instead of the ALB security group ID.
- Accidentally left a NAT Gateway running continuously over a weekend, generating surprise AWS egress charges.
- Associated the private subnets explicitly with the private route table targeting the NAT Gateway.
- Tightened the instance security group so ingress referenced the load balancer security group. The recording does not show the security group id, so I am not publishing one.
- Created AWS budget billing alerts at $5 thresholds and scripted teardown procedures.
Takeaways & Future Iterations
- Security groups are stateful (return traffic is automatically allowed); Network ACLs are stateless (both inbound and outbound rules must be explicitly allowed).
- How to connect to private EC2 instances using AWS Systems Manager (SSM) Session Manager without needing a bastion host or open SSH port 22.
I would codify the entire topology into Terraform modules with variables for CIDR blocks and AZ count rather than provisioning via the AWS console.