VPC Basics: Subnets, Routes & Gateways
Every EC2 instance, RDS database and load balancer you create sits inside a network. On AWS that network is an Amazon Virtual Private Cloud (VPC): your own isolated section of the AWS cloud, where you decide the IP address range, which parts can reach the internet and which stay private.
In this lesson you will learn the parts of a VPC, plan subnets with Python, build a small VPC with the CLI, and see how private servers reach the internet and AWS services safely. You should know what an IP address and CIDR block are; the IP Addresses, Subnets & Ports lesson explains them.
The parts of a VPC
- VPC: a private network in one Region with an IPv4 range in CIDR form, such as
10.20.0.0/16(65,536 addresses). It spans all Availability Zones in the Region. - Subnet: a slice of the VPC's range that lives in one AZ. AWS reserves 5 addresses in every subnet (the network address, the router, DNS, one for future use, and the last address).
- Route table: rules that say where traffic for each destination goes. Every subnet is associated with one route table.
- Internet gateway (IGW): connects the VPC to the internet. A subnet is public if its route table sends
0.0.0.0/0to an internet gateway; otherwise it is private. - NAT gateway: lets instances in private subnets start connections to the internet (for updates or calling an API) while blocking connections from the internet to them.
- VPC endpoints: private connections from your VPC to AWS services such as S3 and DynamoDB without going over the internet.
Every Region gives your account a default VPC (172.31.0.0/16) with a public default subnet in each AZ and an internet gateway. It is handy for quick experiments, but real projects should use a VPC you design.
Plan the address ranges
A common layout has three tiers, each spread over the Availability Zones: public subnets for load balancers, private app subnets for servers or containers, and private data subnets for databases. Python's ipaddress module makes planning easy:
import ipaddress
vpc = ipaddress.ip_network("10.20.0.0/16")
zones = ["ap-south-1a", "ap-south-1b", "ap-south-1c"]
tiers = ["public", "app", "data"]
blocks = vpc.subnets(new_prefix=24) # 256 possible /24 subnets
print(f"VPC {vpc} has {vpc.num_addresses} addresses")
for tier in tiers:
for zone in zones:
subnet = next(blocks)
usable = subnet.num_addresses - 5 # AWS reserves 5 per subnet
print(f"{tier:<7}{zone} {str(subnet):<15} {usable} usable")VPC 10.20.0.0/16 has 65536 addresses public ap-south-1a 10.20.0.0/24 251 usable public ap-south-1b 10.20.1.0/24 251 usable public ap-south-1c 10.20.2.0/24 251 usable app ap-south-1a 10.20.3.0/24 251 usable app ap-south-1b 10.20.4.0/24 251 usable app ap-south-1c 10.20.5.0/24 251 usable data ap-south-1a 10.20.6.0/24 251 usable data ap-south-1b 10.20.7.0/24 251 usable data ap-south-1c 10.20.8.0/24 251 usable
Choose a VPC range that does not overlap your office network or other VPCs you may connect later. Overlapping ranges cannot be joined with peering or VPN, and changing them later is painful.
Build a small VPC with the CLI
The console's "VPC and more" wizard builds all of this in one screen. Doing it once with the CLI shows what the wizard creates. Each command uses --query to print only the new ID:
aws ec2 create-vpc --cidr-block 10.20.0.0/16 \
--tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=shop-vpc}]" \
--query Vpc.VpcId --output text
aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f67890 \
--cidr-block 10.20.0.0/24 --availability-zone ap-south-1a \
--query Subnet.SubnetId --output text
aws ec2 create-internet-gateway --query InternetGateway.InternetGatewayId --output textvpc-0a1b2c3d4e5f67890 subnet-0a1b2c3d4e5f67890 igw-0a1b2c3d4e5f67890
Now attach the gateway, create a route table with a default route to it, and associate the table with the subnet, which makes the subnet public:
aws ec2 attach-internet-gateway --vpc-id vpc-0a1b2c3d4e5f67890 \
--internet-gateway-id igw-0a1b2c3d4e5f67890
aws ec2 create-route-table --vpc-id vpc-0a1b2c3d4e5f67890 \
--query RouteTable.RouteTableId --output text
aws ec2 create-route --route-table-id rtb-0a1b2c3d4e5f67890 \
--destination-cidr-block 0.0.0.0/0 --gateway-id igw-0a1b2c3d4e5f67890
aws ec2 associate-route-table --route-table-id rtb-0a1b2c3d4e5f67890 \
--subnet-id subnet-0a1b2c3d4e5f67890rtb-0a1b2c3d4e5f67890
{
"Return": true
}
{
"AssociationId": "rtbassoc-0a1b2c3d4e5f67890",
"AssociationState": {
"State": "associated"
}
}The route table now has two routes: the automatic local route (10.20.0.0/16), which lets every subnet in the VPC talk to every other, and the default route to the internet gateway.
Private subnets and NAT
Instances in private subnets have no public IP addresses and no route to the internet gateway, so nobody on the internet can reach them. When they need to download updates, send their outbound traffic through a NAT gateway. Traditionally you place one NAT gateway in a public subnet in each AZ and add a route 0.0.0.0/0 -> nat-... to each private route table. Since November 2025, a NAT gateway can also run in regional availability mode, where one gateway automatically expands to every AZ that has workloads and does not need a public subnet.
NAT gateways are charged per hour and per GB processed, so they are one of the first things to delete after practice.
VPC endpoints
Traffic from a private subnet to S3 or DynamoDB does not need the internet at all. A gateway endpoint adds a route for those services to your route tables:
aws ec2 create-vpc-endpoint --vpc-id vpc-0a1b2c3d4e5f67890 \
--service-name com.amazonaws.ap-south-1.s3 \
--route-table-ids rtb-0b1c2d3e4f5a67890 \
--query VpcEndpoint.VpcEndpointId --output textvpce-0a1b2c3d4e5f67890
For most other services, interface endpoints (AWS PrivateLink) place a private network interface for the service inside your subnets. Endpoints keep traffic on the AWS network and reduce NAT gateway data charges.
Common mistakes
- Putting databases in public subnets. Databases belong in private subnets with no route to the internet gateway.
- Forgetting the route. A subnet with an internet gateway attached to the VPC but no
0.0.0.0/0route is still private. - Using a tiny or overlapping CIDR range, which you cannot easily change later.
- One NAT gateway for three AZs. If its AZ fails, private subnets in the other AZs lose internet access.
- Leaving NAT gateways running after experiments.
Key points
- A VPC is your isolated network in one Region; each subnet lives in one Availability Zone.
- AWS reserves 5 IP addresses in every subnet.
- A subnet is public when its route table sends
0.0.0.0/0to an internet gateway. - Private subnets reach the internet outbound through a NAT gateway; nothing can connect to them from the internet.
- Gateway endpoints connect privately to S3 and DynamoDB; interface endpoints cover other services.
- Spread each tier (public, app, data) across at least two AZs.
Exercise
Plan a VPC with the range 10.50.0.0/16 for a hospital appointment app in Hyderabad (ap-south-2a, ap-south-2b). You need public and private subnets in both AZs, each a /24. Write the four subnet ranges, then the CLI command that creates the private subnet in ap-south-2b.
Show solution
public ap-south-2a 10.50.0.0/24
public ap-south-2b 10.50.1.0/24
private ap-south-2a 10.50.10.0/24
private ap-south-2b 10.50.11.0/24aws ec2 create-subnet --region ap-south-2 --vpc-id vpc-0a1b2c3d4e5f67890 \
--cidr-block 10.50.11.0/24 --availability-zone ap-south-2b \
--tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=private-2b}]"Leaving a gap between public and private ranges (0-1 and 10-11) makes the plan easy to read and leaves room to add more subnets later. The private subnet becomes private simply by using a route table without an internet gateway route.
Quick Quiz
1. What makes a subnet public?
Public or private depends entirely on the route table.
2. How many IP addresses does AWS reserve in each subnet?
AWS reserves the first four addresses and the last one in every subnet.
3. A server in a private subnet must download security updates. What does it need?
NAT allows outbound connections from private subnets while blocking inbound ones from the internet.
4. Which lets private instances reach S3 without the internet?
Gateway endpoints add a private route from your VPC to S3 or DynamoDB.
Learn AWS with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
