Phase 3 · CLOUD
VPC 1 — subnets, route tables, gateways
By the end of today
- Explain what a VPC, a CIDR block and a subnet are
- Build a VPC with a public subnet from the AWS CLI
- Read a route table and trace the path to the internet gateway
VPCs, subnets, and the route table that decides who reaches the internet
A VPC (Virtual Private Cloud) is your own private slice of the AWS network — a software-defined network, isolated from every other customer’s, where your servers, databases and load balancers live. Nothing you launch in AWS runs “on the internet” directly; it runs inside a VPC, and you decide which parts of that network can reach out, be reached, or talk only to each other. Get this wrong and a database meant to be private ends up exposed; get it right and the network is the first, quietest layer of your security.
Four pieces build the network you’ll create today:
- The VPC and its CIDR block. When you create a VPC you hand it a private IP range in CIDR notation —
10.0.0.0/16gives you 65,536 addresses to carve up. This is the same CIDR math from Phase 2’s networking week; pick a private range (RFC 1918:10.x,172.16–31.x,192.168.x) and make it big enough to subdivide. - Subnets. A subnet is a smaller slice of the VPC’s range pinned to one availability zone —
10.0.1.0/24is 256 addresses inside the/16. You put resources in subnets, not in the VPC directly. - The internet gateway (IGW). A VPC-wide door to the public internet. One per VPC, attached to it. Without one, nothing inside can reach out and nothing outside can reach in.
- Route tables. A set of rules — “traffic for this destination goes there” — attached to each subnet. This is the piece that decides everything.
Here’s the idea that ties it together and that interviewers love: a subnet is “public” or “private” purely because of its route table. There is no checkbox. A public subnet is one whose route table sends 0.0.0.0/0 (everywhere else) to the internet gateway. A private subnet’s route table has no such route, so its traffic never leaves the VPC. Same subnet machinery, one routing rule apart.
Real world: A VPC is a gated office building you rent a whole floor of. Subnets are the rooms on that floor. The internet gateway is the building’s front door to the street. The route table is the sign by each room’s exit: one room’s sign says “for anywhere outside, take the front door” — that’s a public room — and another room has no such sign at all, so whatever’s inside can only walk to other rooms, never out to the street.
Every new AWS account ships with a default VPC in each region — a 172.31.0.0/16 with a public subnet, an internet gateway and routes already wired, so an EC2 instance launched with no network settings just works. That convenience is exactly why day 48’s box got a public IP automatically, with zero network config (a public IPv4 now carries ~$0.005/hr). Today you build the same thing by hand, so you understand every piece the default hid from you.
For an instance to actually reach the internet three things must all be true: it sits in a subnet whose route table points 0.0.0.0/0 at an IGW, it has a public IP, and its security group allows the traffic. Miss any one and it’s unreachable. Today you wire the first two; the security group you already met on day 48.
Hands-On Lab
What this costs: ₹0. A VPC, its subnets, route tables and an internet gateway are all free — AWS only charges for resources that run inside the network, and this lab launches none (no EC2, and no NAT gateway — that one does bill by the hour, and you’ll meet it tomorrow). There’s nothing billable to leave running here, but you’ll still tear the whole VPC down at the end so nothing lingers in your account.
Budget about 30 minutes. Drive this from the AWS CLI v2 as your Day 47 IAM user (not root), in us-east-1. Every VPC resource returns an ID you capture into a shell variable and pass to the next step — read each ID before moving on. All IDs below are examples; yours will differ.
# 1. Confirm you're your Day 47 IAM user (NOT root) and pin the region.
aws sts get-caller-identity
export AWS_DEFAULT_REGION=us-east-1
# Output (your Account and Arn will differ):
# {
# "UserId": "AIDA...EXAMPLE",
# "Account": "123456789012",
# "Arn": "arn:aws:iam::123456789012:user/devops-you"
# }
# 2. Create a VPC with a /16 private range; capture its ID and turn on DNS hostnames.
VPC=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=m90-vpc}]' \
--query 'Vpc.VpcId' --output text)
aws ec2 modify-vpc-attribute --vpc-id "$VPC" --enable-dns-hostnames
echo "$VPC"
# Output (your VPC ID will differ):
# vpc-0a1b2c3d4e5f67890
# 3. Carve a /24 public subnet (256 addresses) out of the VPC range, in one AZ.
SUBNET=$(aws ec2 create-subnet --vpc-id "$VPC" \
--cidr-block 10.0.1.0/24 --availability-zone us-east-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=m90-public}]' \
--query 'Subnet.SubnetId' --output text)
echo "$SUBNET"
# Output (yours will differ):
# subnet-0abc123def4567890
# 4. Create an internet gateway and attach it to the VPC — the door to the internet.
IGW=$(aws ec2 create-internet-gateway \
--query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway --internet-gateway-id "$IGW" --vpc-id "$VPC"
echo "$IGW"
# Output (yours will differ):
# igw-0abc123def4567890
# 5. Create a route table and add the default route: everywhere (0.0.0.0/0) -> the IGW.
RTB=$(aws ec2 create-route-table --vpc-id "$VPC" \
--query 'RouteTable.RouteTableId' --output text)
aws ec2 create-route --route-table-id "$RTB" \
--destination-cidr-block 0.0.0.0/0 --gateway-id "$IGW"
echo "$RTB"
# Output — create-route returns {"Return": true}, then echo prints your ID:
# rtb-0abc123def4567890
# 6. Associate the route table with the subnet — THIS is what makes the subnet public.
aws ec2 associate-route-table --route-table-id "$RTB" --subnet-id "$SUBNET"
aws ec2 modify-subnet-attribute --subnet-id "$SUBNET" --map-public-ip-on-launch
# Output (the association ID confirms the link; yours will differ):
# {
# "AssociationId": "rtbassoc-0abc123def4567890",
# "AssociationState": { "State": "associated" }
# }
# 7. Read the route table back — the skill today. Two routes: local + the door out.
aws ec2 describe-route-tables --route-table-id "$RTB" \
--query 'RouteTables[0].Routes' --output table
# Output — 'local' is added automatically; the 0.0.0.0/0 -> igw line is the one you added:
# --------------------------------------------------------
# | DescribeRouteTables |
# +-------------------+---------------------+------------+
# | DestinationCidrBlock | GatewayId | State |
# +-------------------+---------------------+------------+
# | 10.0.0.0/16 | local | active |
# | 0.0.0.0/0 | igw-0abc123def... | active |
# +-------------------+---------------------+------------+
# 8. Tear-down, part 1: delete the subnet, then the route table (its association drops with the subnet).
aws ec2 delete-subnet --subnet-id "$SUBNET"
aws ec2 delete-route-table --route-table-id "$RTB"
echo "subnet + route table gone"
# Output:
# subnet + route table gone
# 9. Tear-down, part 2: detach and delete the IGW, then delete the VPC itself.
aws ec2 detach-internet-gateway --internet-gateway-id "$IGW" --vpc-id "$VPC"
aws ec2 delete-internet-gateway --internet-gateway-id "$IGW"
aws ec2 delete-vpc --vpc-id "$VPC"
echo "VPC fully deleted — nothing left behind"
# Output:
# VPC fully deleted — nothing left behind
Read it back: you built a VPC, carved a subnet, hung an internet gateway, and wrote one route — 0.0.0.0/0 → IGW — that turned an ordinary subnet into a public one. Step 7’s route table is the whole lesson in two lines: local for traffic inside the VPC, and the door out to everything else. Then you deleted every piece, in dependency order, so nothing lingers.
Common Errors & Fixes
These three catch almost everyone building their first VPC by hand. Read the error text slowly — parsing it is the actual skill.
Common error: Adding a
0.0.0.0/0route that points at an internet gateway before the gateway has been attached to the VPC:An error occurred (Gateway.NotAttached) when calling the CreateRoute operation: resource igw-0abc123def4567890 is not attached to network vpc-0a1b2c3d4e5f67890Why: A route table lives in a VPC, so a route inside it can only point at a gateway that belongs to that same VPC. Creating an internet gateway does not attach it — it’s born unattached, floating free until you wire it to a VPC.
Fix: Run
aws ec2 attach-internet-gateway --internet-gateway-id "$IGW" --vpc-id "$VPC"first (step 4), then create the route (step 5). The order is create → attach → route, never create → route.How you’d spot it in prod:
Gateway.NotAttachedin a Terraform apply almost always means a missing dependency edge — the route resource ran before the gateway attachment. The fix is ordering (an explicitdepends_onor referencing the attachment), not the route itself.
Common error: Trying to delete the VPC while it still has a subnet, gateway or route table inside it:
An error occurred (DependencyViolation) when calling the DeleteVpc operation: The vpc 'vpc-0a1b2c3d4e5f67890' has dependencies and cannot be deleted.Why: A VPC is a container. AWS refuses to delete it while anything still lives inside — subnets, a custom route table, an attached internet gateway, network interfaces — so you can’t orphan resources or leave dangling references behind.
Fix: Remove the children first, in order: delete subnets, delete custom route tables, then detach and delete the internet gateway, and only then
delete-vpc. Steps 8–9 do exactly this — subnet and route table, then IGW, then the VPC last.How you’d spot it in prod: A
terraform destroythat hangs or fails onDependencyViolationmeans something is still attached — often an ENI left by a load balancer or a Lambda. Find and remove the child resource; forcing the parent delete never works.
Common error: Creating a VPC with a CIDR block outside the range AWS permits — too large:
An error occurred (InvalidVpc.Range) when calling the CreateVpc operation: The CIDR '10.0.0.0/8' is invalid.Why: AWS caps a VPC’s IPv4 CIDR between
/16(65,536 addresses) and/28(16 addresses). A/8asks for 16 million addresses, far beyond the limit, so the call is rejected before the VPC is ever created.Fix: Choose a block in range —
10.0.0.0/16is the safe default this lab uses. If you need more addresses than a/16later, you add a secondary CIDR block to the VPC rather than widening the prefix.How you’d spot it in prod:
InvalidVpc.Range(or the matchingInvalidSubnet.Rangefor a subnet outside its VPC’s block) is a planning error caught at apply time — someone typed a prefix outside/16–/28, or a subnet range that isn’t contained in the VPC. Fix the number, not the API call.
VPC Interview Questions
Cover the answers below and say your own version out loud first — define a VPC, a CIDR block, and what actually makes a subnet public before you reveal each answer. Recalling before revealing is what makes these stick when an interviewer asks them cold. The four questions and answers render right after this note.
Go Deeper
Optional extras if you have ~30 more minutes today:
- 5 min — Find your account’s default VPC (
aws ec2 describe-vpcs --filters Name=isDefault,Values=true) and read its route table. Compare it to the one you built by hand — samelocal+0.0.0.0/0 → igwshape, wired for you. - 10 min — Read the AWS VPC docs on route tables: the difference between the main route table (the implicit default every subnet uses) and a custom one you associate explicitly, like today’s.
- 15 min — Add a second subnet in a different availability zone (
us-east-1b), associate it with the same route table, and confirm both are public — the two-AZ layout every real load-balanced app starts from. Delete both when done.
What is a VPC, and why doesn't everything just run on a public IP? Both
A VPC, a Virtual Private Cloud, is your own isolated slice of the AWS network — a software-defined network where your instances, databases and load balancers live, walled off from every other customer. Everything you launch runs inside a VPC; nothing sits directly on the internet. The point is control and isolation: you decide which parts of the network can reach out, be reached, or only talk to each other. A database in a private subnet with no route out simply can't be reached from the internet, no matter who scans it. Running everything on public IPs would mean every resource is exposed by default — the VPC lets you expose only what you choose.
What makes a subnet public or private? Both
Its route table — nothing else. There's no 'public' checkbox on a subnet. A subnet is public when its associated route table has a route sending 0.0.0.0/0 to an internet gateway; that's the door out to the internet. A private subnet's route table has no such route, so its traffic never leaves the VPC. The exact same subnet becomes public or private purely by which route table you attach and what's in it. In practice you put web servers and load balancers in public subnets, and databases and internal services in private ones — then control the reachable path with route tables and security groups.
What is a CIDR block, and how do you pick the range for a VPC? Both
A CIDR block is an IP range written as an address plus a prefix length — 10.0.0.0/16 means the first 16 bits are fixed, leaving 65,536 addresses to use. For a VPC you choose a private range from RFC 1918 — 10.x, 172.16–31.x, or 192.168.x — so it never clashes with public internet addresses. AWS allows VPC blocks between /16 and /28. I pick something big enough to subdivide into subnets across availability zones with room to grow, and I make sure it doesn't overlap with other VPCs or on-prem networks I might peer or VPN to later — overlapping CIDRs are the classic thing that blocks a peering connection.
An EC2 instance in your subnet can't reach the internet — what do you check? Product
Three things all have to be true, so I check each. First, the route table on that subnet needs a 0.0.0.0/0 route pointing at an internet gateway that's actually attached to the VPC — no route, no exit. Second, the instance needs a public IP; a box in a public subnet without one still can't reach out or be reached. Third, its security group and the subnet's network ACL must allow the traffic. I work outward: local routing, then the gateway route, then the public IP, then the security group. If it's a timeout rather than a refusal, it's almost always the route table or a missing public IP, not the app.
Mark Day 50 complete
Tomorrow you finish the VPC — NAT gateways, private subnets, and the bastion pattern that keeps private boxes reachable without exposing them.
Stuck on today’s lab? Ask in Mission 90 Q&A