VPC Basics: Subnets, Routes & Gateways

AWS · Lesson 13 of 30 7 min read Updated

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/0 to 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:

Python
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")
Output
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:

Terminal
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 text
Output
vpc-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:

Terminal
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-0a1b2c3d4e5f67890
Output
rtb-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:

Terminal
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 text
Output
vpce-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/0 route 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/0 to 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
Text
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/24
Terminal
aws 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?

2. How many IP addresses does AWS reserve in each subnet?

3. A server in a private subnet must download security updates. What does it need?

4. Which lets private instances reach S3 without the internet?

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