Prerequisites
- An AWS account
- Basic understanding of IP addressing and CIDR notation (e.g. what /24 means)
What You'll Learn
- Plan a VPC's CIDR range and divide it into public and private subnets across AZs
- Configure route tables so private subnets reach the internet only through a NAT gateway
- Explain the difference between security groups (stateful) and NACLs (stateless)
- Design a 2-tier VPC layout for a real application
Theory
A VPC is a logically isolated network you define within a single AWS region, starting from a CIDR block (e.g. 10.0.0.0/16 — 65,536 addresses). Subnets carve that range into smaller blocks, each pinned to exactly one Availability Zone. A subnet is "public" only by convention — specifically, because its route table sends 0.0.0.0/0 traffic to an Internet Gateway; nothing else structurally distinguishes it from a private subnet.
Route tables are the actual mechanism
Every subnet is associated with exactly one route table. A public subnet's route table has a route for 0.0.0.0/0 → igw-xxxx. A private subnet's route table instead sends 0.0.0.0/0 → nat-xxxx (a NAT Gateway sitting in a public subnet), so instances can initiate outbound connections (package updates, API calls) but cannot be reached by unsolicited inbound traffic from the internet.
Security Groups vs. Network ACLs
| Security Group | Network ACL | |
|---|---|---|
| Applies to | ENI (instance level) | Subnet level |
| State | Stateful — return traffic auto-allowed | Stateless — must allow both directions explicitly |
| Rules | Allow only | Allow and Deny, evaluated in rule-number order |
| Typical use | Primary day-to-day access control | Coarse subnet-wide backstop |
Architecture
A standard 2-AZ, 2-tier VPC: public subnets hold the ALB and NAT Gateways, private subnets hold app and database instances
Hands-On Lab
# 1. Create the VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query Vpc.VpcId --output text
# => vpc-0abc123
# 2. Create one public and one private subnet
aws ec2 create-subnet --vpc-id vpc-0abc123 --cidr-block 10.0.1.0/24 --availability-zone us-east-1a
aws ec2 create-subnet --vpc-id vpc-0abc123 --cidr-block 10.0.11.0/24 --availability-zone us-east-1a
# 3. Create and attach an Internet Gateway
aws ec2 create-internet-gateway --query InternetGateway.InternetGatewayId --output text
aws ec2 attach-internet-gateway --vpc-id vpc-0abc123 --internet-gateway-id igw-0def456
# 4. Create a public route table and route 0.0.0.0/0 to the IGW
aws ec2 create-route-table --vpc-id vpc-0abc123 --query RouteTable.RouteTableId --output text
aws ec2 create-route --route-table-id rtb-0ghi789 --destination-cidr-block 0.0.0.0/0 --gateway-id igw-0def456
aws ec2 associate-route-table --route-table-id rtb-0ghi789 --subnet-id subnet-0public
# 5. Create a NAT Gateway in the public subnet (requires an Elastic IP), then
# route the private subnet's 0.0.0.0/0 traffic to it the same way as step 4Best Practices
Always span at least two Availability Zones
A single-AZ VPC has no protection against an AZ-level outage — mirror every public/private subnet pair into a second AZ from day one, even for small workloads.Reserve CIDR space for growth
A /16 VPC divided into a handful of /24 subnets still leaves enormous headroom for future subnets (additional tiers, VPC peering ranges) — starting too small forces a disruptive re-IP later.Common Mistakes
Putting a database directly in a public subnet 'for simplicity'
Even with a locked-down security group, a public subnet means the instance has a route to the internet at all — a single misconfigured security group rule then exposes it directly. Databases belong in private subnets, full stop.One NAT Gateway shared across all AZs
A NAT Gateway lives in one AZ; if every private subnet in every AZ routes through a single NAT Gateway, that AZ's failure takes down outbound connectivity for the whole VPC. Deploy one NAT Gateway per AZ for real high availability (at the cost of running more of them).Troubleshooting
An EC2 instance in a private subnet can't reach the internet: check, in order: the subnet's route table has a 0.0.0.0/0route to a NAT Gateway; the NAT Gateway itself is in a public subnet whose route table points to an IGW; the instance's security group allows the outbound traffic; and the NACL (if customized) allows it in both directions.
Two instances in the same subnet can't reach each other: since they share a route table, this is almost always a security group rule — remember SGs are stateful but still need an explicit inbound allow rule on the receiving instance for the initiating traffic.
Interview Questions
Frequently Asked Questions
Summary
A subnet is "public" purely because its route table points 0.0.0.0/0 at an Internet Gateway; private subnets route outbound traffic through a NAT Gateway instead. Security groups (stateful, instance-level) handle day-to-day access control, NACLs (stateless, subnet-level) act as a coarser backstop — and every production VPC should span at least two Availability Zones with a NAT Gateway in each.