VPC basics: CIDR, subnets, route tables and the internet gateway
-
Phase 0: Before you start
-
Phase 1: The foundation
-
Phase 2: Operations and security
-
Phase 3: SQL Server on AWS
-
Phase 4: Troubleshooting step by step
-
Phase 5: Final project and career
Every server, database and load balancer in this course lives inside one private network. This AWS VPC tutorial explains CIDR blocks, subnets, route tables and internet gateways in plain words, then you build saat-vpc from the command line.
A VPC and its subnets cost nothing, so this network stays in your account for the rest of the course. You’ll save its IDs in a file that later lessons read.
| Time | You need | By the end you can |
|---|---|---|
| 60 minutes | Your laptop signed in with aws login --profile saat-admin. | Plan IP ranges with CIDR, build a VPC with public, private and database subnets in two zones, route a public subnet to the internet, and fix overlapping or out-of-range subnets. |
What it is, in one line
A VPC is your own private network in AWS, split into subnets, with route tables that decide where traffic can go.
In Simple Words
A gated housing society
Think of a gated housing society. The whole society has a range of house numbers, say 1 to 65,000. That range is the VPC’s CIDR block.
The society is split into blocks: Block A near the main gate for shops, Block B for homes, Block C behind the homes for the bank vault. Those are subnets. Only Block A has a road to the main gate.
The signboards at each junction (“To main gate: turn left”) are route tables. The main gate itself is the internet gateway. A house in Block B can’t reach the street, not because of a wall, but because no signboard points to the gate.
Engineer's View
How a VPC is put together
A VPC belongs to one Region and has an IPv4 range written in CIDR notation, like 10.20.0.0/16. The number after the slash is how many bits are fixed: a /16 has 65,536 addresses, a /24 has 256, a /28 has 16. VPC sizes run from /16 to /28.
Use private ranges (10.x, 172.16 to 172.31, 192.168.x) that don’t overlap your office network or other VPCs you may connect later.
A subnet is a slice of the VPC range in one Availability Zone. AWS keeps five addresses in every subnet (network, router, DNS, one reserved, broadcast), so a /24 gives 251 usable addresses.
Every subnet uses exactly one route table. Each table has a local route for the whole VPC, so all subnets can talk to each other. A subnet is public only when its route table sends 0.0.0.0/0 to an internet gateway and its instances have public IPs.
Subnets without an explicit association use the VPC’s main route table.
Public IPv4 addresses have an hourly charge (about USD 0.005 an hour each since 2024), so only public subnets assign them automatically in our design.
How it works, step by step
Here is the design you are about to build. Read it once before you type anything.
1Choose the range
10.20.0.0/16 is big enough for years of labs and unlikely to clash with home routers (usually 192.168.x) or common office ranges (10.0.x).
2Cut it into layers and zones
Three layers: public (load balancers, bastions), private (app servers), database. Each layer gets one /24 in ap-south-1a and one in ap-south-1b, so a zone failure leaves half of everything running.
3Give the public layer a way out
An internet gateway attached to the VPC, plus a route table saat-rt-public with 0.0.0.0/0 pointing at it, used only by the two public subnets.
4Keep the others inside
saat-rt-private has only the local route. Private servers get outbound internet through a NAT gateway in Lesson 9; databases never get it.
Remember: a subnet’s name means nothing to AWS. This is the only test that matters.
When someone says “my instance in the public subnet can’t reach the internet”, check three things in this order: the route table of that subnet, whether the instance has a public IP, and the security group (Lesson 10).
✅ Checkpoint: You can say how many usable addresses a /24 has in AWS (251) and what makes a subnet public.
Hands-on lab: AWS VPC tutorial, build saat-vpc
Start as usual:
cd C:\aws-lab
aws login --profile saat-admin
$env:AWS_PROFILE = 'saat-admin'1Do the CIDR maths first
Paste this whole block:
function Get-SubnetInfo([string]$Cidr) {
$ip, $bits = $Cidr -split '/'
$size = [math]::Pow(2, 32 - [int]$bits)
[pscustomobject]@{ Cidr = $Cidr; Addresses = $size; UsableInAws = $size - 5 }
}
'10.20.0.0/16','10.20.1.0/24','10.20.21.0/28' | ForEach-Object { Get-SubnetInfo $_ } | Format-TableYou should see this:
Cidr Addresses UsableInAws
---- --------- -----------
10.20.0.0/16 65536 65531
10.20.1.0/24 256 251
10.20.21.0/28 16 112Create the VPC
Run this:
$vpc = aws ec2 create-vpc --cidr-block 10.20.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=saat-vpc},{Key=course,Value=saatvedha-aws},{Key=lesson,Value=08}]' --query Vpc.VpcId --output text
aws ec2 modify-vpc-attribute --vpc-id $vpc --enable-dns-hostnames Value=true
$vpcYou should see something like this:
vpc-0f1e2d3c4b5a697883Create the six subnets
Paste this whole block:
$plan = @(
@{ Name = 'saat-public-a'; Cidr = '10.20.1.0/24'; Az = 'ap-south-1a' },
@{ Name = 'saat-public-b'; Cidr = '10.20.2.0/24'; Az = 'ap-south-1b' },
@{ Name = 'saat-private-a'; Cidr = '10.20.11.0/24'; Az = 'ap-south-1a' },
@{ Name = 'saat-private-b'; Cidr = '10.20.12.0/24'; Az = 'ap-south-1b' },
@{ Name = 'saat-db-a'; Cidr = '10.20.21.0/24'; Az = 'ap-south-1a' },
@{ Name = 'saat-db-b'; Cidr = '10.20.22.0/24'; Az = 'ap-south-1b' }
)
$ids = @{}
foreach ($s in $plan) {
$ids[$s.Name] = aws ec2 create-subnet --vpc-id $vpc --cidr-block $s.Cidr --availability-zone $s.Az --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$($s.Name)},{Key=course,Value=saatvedha-aws},{Key=lesson,Value=08}]" --query Subnet.SubnetId --output text
}
$ids4Add the internet gateway and the public route table
Paste this whole block:
$igw = aws ec2 create-internet-gateway --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=saat-igw},{Key=course,Value=saatvedha-aws},{Key=lesson,Value=08}]' --query InternetGateway.InternetGatewayId --output text
aws ec2 attach-internet-gateway --internet-gateway-id $igw --vpc-id $vpc
$rtPub = aws ec2 create-route-table --vpc-id $vpc --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=saat-rt-public},{Key=course,Value=saatvedha-aws},{Key=lesson,Value=08}]' --query RouteTable.RouteTableId --output text
aws ec2 create-route --route-table-id $rtPub --destination-cidr-block 0.0.0.0/0 --gateway-id $igw
foreach ($n in 'saat-public-a','saat-public-b') {
aws ec2 associate-route-table --route-table-id $rtPub --subnet-id $ids[$n] | Out-Null
aws ec2 modify-subnet-attribute --subnet-id $ids[$n] --map-public-ip-on-launch
}The create-route command should print:
{
"Return": true
}5Add the private route table
Paste this whole block:
$rtPriv = aws ec2 create-route-table --vpc-id $vpc --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=saat-rt-private},{Key=course,Value=saatvedha-aws},{Key=lesson,Value=08}]' --query RouteTable.RouteTableId --output text
foreach ($n in 'saat-private-a','saat-private-b','saat-db-a','saat-db-b') {
aws ec2 associate-route-table --route-table-id $rtPriv --subnet-id $ids[$n] | Out-Null
}6Check the result
Run this:
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$vpc" --query "Subnets[].[Tags[?Key=='Name']|[0].Value,CidrBlock,AvailabilityZone,MapPublicIpOnLaunch]" --output text | Sort-ObjectYou should see this:
saat-db-a 10.20.21.0/24 ap-south-1a False
saat-db-b 10.20.22.0/24 ap-south-1b False
saat-private-a 10.20.11.0/24 ap-south-1a False
saat-private-b 10.20.12.0/24 ap-south-1b False
saat-public-a 10.20.1.0/24 ap-south-1a True
saat-public-b 10.20.2.0/24 ap-south-1b True
7Save the IDs for later lessons
Run this:
[pscustomobject]@{
VpcId = $vpc; IgwId = $igw; RtPublic = $rtPub; RtPrivate = $rtPriv
PublicA = $ids['saat-public-a']; PublicB = $ids['saat-public-b']
PrivateA = $ids['saat-private-a']; PrivateB = $ids['saat-private-b']
DbA = $ids['saat-db-a']; DbB = $ids['saat-db-b']
} | ConvertTo-Json | Set-Content C:\aws-lab\saat-vpc.json
Get-Content C:\aws-lab\saat-vpc.jsonFrom now on, any lesson can load the network with $v = Get-Content C:\aws-lab\saat-vpc.json | ConvertFrom-Json.
✅ Checkpoint: Six subnets exist, only the two public ones say True, the public route table has 0.0.0.0/0 to saat-igw, and saat-vpc.json holds all ten IDs. Don’t delete this VPC; it costs nothing and you need it from Lesson 9 on.
Break It, Then Fix It
CIDR mistakes are the most common errors when building networks, and AWS catches both of these immediately.
1Break it: a subnet that overlaps another
Try to create a subnet inside saat-public-a’s range:
aws ec2 create-subnet --vpc-id $vpc --cidr-block 10.20.1.128/25 --availability-zone ap-south-1aYou’ll get this error:
An error occurred (InvalidSubnet.Conflict) when calling the CreateSubnet operation: The CIDR '10.20.1.128/25' conflicts with another subnet10.20.1.128/25 is the top half of 10.20.1.0/24. Subnets in one VPC can never share addresses. Fix: pick an unused block, for example 10.20.3.0/24, and keep a written plan like the one in step 3 so everyone uses the next unused number.
2Break it: a subnet outside the VPC range
Run this:
aws ec2 create-subnet --vpc-id $vpc --cidr-block 10.30.1.0/24 --availability-zone ap-south-1aYou’ll get this error:
An error occurred (InvalidSubnet.Range) when calling the CreateSubnet operation: The CIDR '10.30.1.0/24' is invalid.
The message only says “invalid”, but the reason is simple: 10.30.x isn’t inside 10.20.0.0/16. Fix: use a block inside the VPC, or add a secondary CIDR block to the VPC first if you really need more space.
✅ Checkpoint: You saw both CIDR errors and know the fix for each. Nothing new was created, so there is nothing to clean up.
This happened at work
The VPC that matched the office
A logistics company built its first VPC with the console’s suggested range, 10.0.0.0/16. Everything worked for a year. Then the company wanted a Site-to-Site VPN so warehouse systems could reach the cloud app directly.
The head office network was also 10.0.0.0/16. With the same addresses on both sides, routers couldn’t tell which 10.0.5.20 was meant. The VPN came up, but nothing could talk across it.
The team had to build a new VPC with 10.40.0.0/16, rebuild the servers into it and move the database during a weekend window. Since then, every new VPC gets its range from a shared IP plan (later managed in VPC IP Address Manager), and nobody accepts a default range.
Common mistakes and troubleshooting
| Mistake | What happens | What to do |
|---|---|---|
| Using the default 10.0.0.0/16 everywhere | VPN or peering fails later because ranges overlap | Plan ranges per VPC; avoid ranges your office uses |
| VPC too small (for example /24) | No room for new subnets later | Start with /16 for a main VPC; subnets are cheap to add |
| Calling a subnet “public” but not routing it | Instances can’t reach the internet | Route 0.0.0.0/0 to an internet gateway in its route table |
| Forgetting to associate a subnet | It silently uses the main route table | Associate every subnet explicitly |
| Auto-assigning public IPs in private subnets | Extra hourly charges and exposure | Only public subnets get map-public-ip-on-launch |
| Forgetting AWS keeps 5 addresses per subnet | Fewer IPs than expected, launches fail when full | A /24 gives 251 usable addresses, a /28 only 11 |
Interview questions
Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.
Fresher level
1. How would you explain a VPC to someone who has only worked with an office network?
Why they ask this: They want to check you understand the basic network idea in AWS. Everything else in networking builds on it.
How you might answer: I’d say a VPC is a private, isolated network for my account in one Region. I choose its IP range, and I create its subnets, route tables and gateways.
Resources like EC2 instances and RDS databases live inside it, a bit like servers sit inside the office network.
2. A teammate named a subnet “public-subnet”, but nothing in it can reach the internet. What actually makes a subnet public?
Why they ask this: This is one of the most common VPC questions. They want to know you look at routes, not names.
How you might answer: The name doesn’t matter at all. A subnet is public when its route table has a 0.0.0.0/0 route to an internet gateway, and the instances in it have public IP addresses.
So I’d check the route table association first, then whether the instances got a public IP.
3. You create a /24 subnet in AWS. How many IP addresses can you actually use, and why?
Why they ask this: They want to see practical CIDR knowledge. Getting this wrong leads to subnets that fill up sooner than planned.
How you might answer: I can use 251. A /24 has 256 addresses, and AWS reserves 5 in every subnet: the network address, the VPC router, DNS, one for future use, and the broadcast address.
2–5 years experience
4. Why would you bother creating subnets in more than one Availability Zone?
Why they ask this: They want to see high availability thinking. Single-zone designs are a common cause of outages.
How you might answer: A subnet lives in one zone only. If that zone has a problem, everything in that subnet is affected.
With matching subnets in two or three zones, load balancers and Multi-AZ databases can keep working when one zone fails.
5. Someone added an internet route to the main route table, and suddenly several “private” subnets became public. How did that happen?
Why they ask this: They want to know if you understand hidden defaults in a VPC. These quiet defaults cause real security incidents.
How you might answer: The main route table is the one any subnet uses if it has no explicit association. So adding an internet route there made every unassociated subnet public.
To avoid it, I keep the main table private and associate each subnet with a route table explicitly.
6. You’re creating a new VPC. How would you choose its CIDR range?
Why they ask this: They want to see that you plan for growth and future connections. Overlapping ranges are very painful to fix later.
How you might answer: I’d pick a private range that doesn’t overlap the office network, other VPCs, or any partner I may connect to. I’d make it big enough to grow, often a /16.
Then I’d record it in a shared IP plan or in IPAM so nobody else takes it.
Senior level
7. Your company wants 50 VPCs connected to each other and to the data centre. How would you plan the IP addresses?
Why they ask this: They want to see large-scale network design. They also check that you’d remove manual steps that cause mistakes.
How you might answer: I’d set aside a large supernet for AWS that doesn’t clash with on-premises, then split it into pools per Region and per environment in VPC IPAM. Each VPC gets a block that doesn’t overlap any other.
I’d connect them through a Transit Gateway with route tables per environment. Allocation is documented and automated, so nobody picks ranges by hand.
8. A VPC is running out of IP addresses and the app team needs more servers next week. What are your options?
Why they ask this: They want to see problem-solving under pressure. They also check that you try the least disruptive options first.
How you might answer: I’d add a secondary CIDR block to the VPC and create new subnets there. For the next workloads I’d use smaller subnets, and I’d clean up unused ENIs and IPs.
For containers I’d look at prefix delegation or IPv6. Rebuilding the VPC is my last resort.
Quick quiz
Q1. How many addresses does AWS reserve in every subnet?
- 0
- 2
- 5
- 10
Show answer
C — AWS reserves 5 addresses per subnet, so a /24 has 251 usable addresses.
Q2. What makes saat-public-a a public subnet?
- Its name
- Its route table sends 0.0.0.0/0 to an internet gateway
- It is in ap-south-1a
- It has a /24 range
Show answer
B — Only the route to an internet gateway (plus public IPs) makes a subnet public.
Q3. A subnet lives in how many Availability Zones?
- One
- Two
- All zones of the Region
- None
Show answer
A — Each subnet belongs to exactly one zone. That is why you create pairs.
Q4. Creating 10.20.1.128/25 fails with InvalidSubnet.Conflict. Why?
- The VPC is full
- It overlaps 10.20.1.0/24
- /25 isn’t allowed
- Wrong zone
Show answer
B — 10.20.1.128/25 is half of an existing subnet, and subnets can’t overlap.
Q5. Which range is the safest choice for a VPC you may later connect to an office using 10.0.0.0/16?
- 10.0.0.0/16
- 10.0.128.0/17
- 10.20.0.0/16
- 0.0.0.0/0
Show answer
C — 10.20.0.0/16 doesn’t overlap the office range.
Quick recap
- A VPC is a private network in one Region with a CIDR range; saat-vpc is 10.20.0.0/16.
- Subnets are slices of that range in one zone; AWS keeps 5 addresses in each.
- Route tables decide traffic paths. A subnet is public only with 0.0.0.0/0 to an internet gateway.
- Plan ranges so they never overlap networks you may connect later.
- saat-vpc.json holds all the IDs; later lessons load it instead of looking them up.
Next lesson
Lesson 9: Private subnets, NAT gateway and VPC endpoints → You’ll give private servers a safe way out to the internet and learn why NAT gateways show up on bills.
Further reading: AWS VPC tutorial
Want to go deeper into AWS VPC tutorial? Read How Amazon VPC works and Subnet CIDR blocks in the Amazon VPC User Guide. You can also see the full plan for this course on the AWS Cloud Administration course page.
