Subnets & Routes
A subnet is a CIDR block inside a VPC, tied to one Availability Zone. Public subnets route to an Internet Gateway; private subnets don’t. The boundary between the two is the security backbone of every AWS account.
Public + private subnet pair, with route tables
EXAMPLE
# 1) Plan the addressing — RFC-1918, generous, multi-AZ
# VPC: 10.0.0.0/16
# AZ a: public 10.0.0.0/24 private 10.0.16.0/20
# AZ b: public 10.0.1.0/24 private 10.0.32.0/20
# AZ c: public 10.0.2.0/24 private 10.0.48.0/20
# 2) Create VPC + subnets
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=app}]'
# returns vpc-abc123
aws ec2 create-subnet --vpc-id vpc-abc123 --cidr-block 10.0.0.0/24 --availability-zone us-east-1a
aws ec2 create-subnet --vpc-id vpc-abc123 --cidr-block 10.0.16.0/20 --availability-zone us-east-1a
aws ec2 create-subnet --vpc-id vpc-abc123 --cidr-block 10.0.1.0/24 --availability-zone us-east-1b
# …
# 3) Internet gateway (only for PUBLIC subnets)
aws ec2 create-internet-gateway
aws ec2 attach-internet-gateway --vpc-id vpc-abc123 --internet-gateway-id igw-abc
# 4) Route tables
# Public — 0.0.0.0/0 → IGW
aws ec2 create-route-table --vpc-id vpc-abc123 # → rtb-pub
aws ec2 create-route --route-table-id rtb-pub --destination-cidr-block 0.0.0.0/0 --gateway-id igw-abc
aws ec2 associate-route-table --route-table-id rtb-pub --subnet-id subnet-pub-a
# Private — 0.0.0.0/0 → NAT gateway (lives in a public subnet)
aws ec2 allocate-address --domain vpc # eip
aws ec2 create-nat-gateway --subnet-id subnet-pub-a --allocation-id eipalloc-…
aws ec2 create-route-table --vpc-id vpc-abc123 # → rtb-priv
aws ec2 create-route --route-table-id rtb-priv --destination-cidr-block 0.0.0.0/0 --nat-gateway-id nat-…
# 5) Sanity check — what gets where
aws ec2 describe-subnets --filters Name=vpc-id,Values=vpc-abc123 \
--query 'Subnets[*].[SubnetId,AvailabilityZone,CidrBlock,MapPublicIpOnLaunch]' --output table
# 6) Equivalent in Terraform — shape that everyone copies
# resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" }
# resource "aws_subnet" "public" {
# for_each = toset(["a","b","c"])
# vpc_id = aws_vpc.main.id
# availability_zone = "us-east-1${each.key}"
# cidr_block = "10.0.${index(["a","b","c"], each.key)}.0/24"
# map_public_ip_on_launch = true
# }
# 7) Best practices
# • App + DB in private subnets — never public IPs on databases
# • One NAT GW per AZ (HA) — or use NAT instances + autoscaling for cost
# • Use VPC endpoints for S3 / DynamoDB to skip NAT egress charges entirely
# • Tag subnets `kubernetes.io/role/elb` and `…/internal-elb` for EKS
Why it matters
A public subnet is just “has a route to an IGW”; a private subnet is “doesn’t.” The hardest AWS bills come from misrouted private subnets accidentally egressing through a NAT gateway you forgot about.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
# Public subnets have route to internet gateway. # Private subnets reach internet via NAT.Try it Yourself »
Discussion
Loading…