AWS for DevOps — Subnets & Route Tables
4.2 — Subnets & Route Tables
Subnets divide your VPC into segments. Route tables control where network traffic is directed.
Subnets
A subnet lives in one Availability Zone and has its own CIDR range (a subset of the VPC CIDR):
VPC: 10.0.0.0/16
│
├── AZ: ap-south-1a
│ ├── Public: 10.0.1.0/24 (251 usable IPs)
│ ├── Private: 10.0.10.0/24 (251 usable IPs)
│ └── Data: 10.0.20.0/24 (251 usable IPs)
│
├── AZ: ap-south-1b
│ ├── Public: 10.0.2.0/24
│ ├── Private: 10.0.11.0/24
│ └── Data: 10.0.21.0/24
│
└── AZ: ap-south-1c
├── Public: 10.0.3.0/24
├── Private: 10.0.12.0/24
└── Data: 10.0.22.0/24
Creating Subnets
# Public subnet in AZ-a
$ aws ec2 create-subnet \
--vpc-id vpc-0abc123 \
--cidr-block 10.0.1.0/24 \
--availability-zone ap-south-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1a}]'
{
"Subnet": {
"SubnetId": "subnet-pub1a",
"CidrBlock": "10.0.1.0/24",
"AvailabilityZone": "ap-south-1a"
}
}
# Private subnet in AZ-a
$ aws ec2 create-subnet \
--vpc-id vpc-0abc123 \
--cidr-block 10.0.10.0/24 \
--availability-zone ap-south-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-1a}]'
# Enable auto-assign public IP for public subnets
$ aws ec2 modify-subnet-attribute \
--subnet-id subnet-pub1a \
--map-public-ip-on-launch
What Makes a Subnet Public or Private?
A subnet is public if its route table has a route to an Internet Gateway. A subnet is private if it does not.
| Subnet Type | Route to IGW | Route to NAT | Use Case |
|---|---|---|---|
| Public | ✅ Yes | Not needed | ALB, bastion, NAT Gateway |
| Private (with NAT) | ❌ No | ✅ Yes | App servers, ECS tasks |
| Private (isolated) | ❌ No | ❌ No | Databases, internal services |
Route Tables
A route table contains rules (routes) that determine where traffic goes:
Public Route Table:
┌──────────────────┬─────────────────────┐
│ Destination │ Target │
├──────────────────┼─────────────────────┤
│ 10.0.0.0/16 │ local │
│ 0.0.0.0/0 │ igw-abc123 │
└──────────────────┴─────────────────────┘
Private Route Table:
┌──────────────────┬─────────────────────┐
│ Destination │ Target │
├──────────────────┼─────────────────────┤
│ 10.0.0.0/16 │ local │
│ 0.0.0.0/0 │ nat-def456 │
└──────────────────┴─────────────────────┘
localroute — traffic within the VPC stays within the VPC (always present, cannot be removed)0.0.0.0/0— default route for all non-VPC traffic
Creating Route Tables
# Create a public route table
$ aws ec2 create-route-table \
--vpc-id vpc-0abc123 \
--tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=public-rt}]'
{
"RouteTable": {
"RouteTableId": "rtb-pub123"
}
}
# Add a route to the Internet Gateway
$ aws ec2 create-route \
--route-table-id rtb-pub123 \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-abc123
# Associate the route table with the public subnet
$ aws ec2 associate-route-table \
--route-table-id rtb-pub123 \
--subnet-id subnet-pub1a
Production Subnet Layout
For high availability, repeat your subnet pattern across at least two (ideally three) AZs:
# Create all subnets in a loop (conceptual — run each individually)
# Public subnets
$ for az_suffix in a b c; do
aws ec2 create-subnet \
--vpc-id vpc-0abc123 \
--cidr-block "10.0.${az_suffix_num}.0/24" \
--availability-zone "ap-south-1${az_suffix}" \
--tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=public-1${az_suffix}}]"
done
Listing Subnets
$ aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=vpc-0abc123" \
--query 'Subnets[*].[SubnetId,CidrBlock,AvailabilityZone,Tags[?Key==`Name`].Value|[0]]' \
--output table
-----------------------------------------------------------
| subnet-pub1a | 10.0.1.0/24 | ap-south-1a | public-1a |
| subnet-prv1a | 10.0.10.0/24 | ap-south-1a | private-1a |
| subnet-pub1b | 10.0.2.0/24 | ap-south-1b | public-1b |
| subnet-prv1b | 10.0.11.0/24 | ap-south-1b | private-1b |
-----------------------------------------------------------
Common Mistakes
| Mistake | Why It Hurts | Fix |
|---|---|---|
| All subnets in one AZ | Single AZ failure takes everything down | Spread across 2-3 AZs |
| Overlapping CIDR blocks | Cannot peer VPCs with overlapping ranges | Plan CIDR allocation upfront |
| Not tagging subnets | Can’t tell public from private | Tag: Name, Type=public/private |
| Using the main route table for public routing | New subnets auto-associate and become public | Create explicit route tables |
Quick Reference
| Task | Command |
|---|---|
| Create subnet | aws ec2 create-subnet --vpc-id VPC --cidr-block CIDR --availability-zone AZ |
| Auto-assign public IP | aws ec2 modify-subnet-attribute --subnet-id SUBNET --map-public-ip-on-launch |
| Create route table | aws ec2 create-route-table --vpc-id VPC |
| Add route | aws ec2 create-route --route-table-id RT --destination-cidr-block CIDR --gateway-id GW |
| Associate RT to subnet | aws ec2 associate-route-table --route-table-id RT --subnet-id SUBNET |
| List subnets | aws ec2 describe-subnets --filters Name=vpc-id,Values=VPC |
Next → Internet & NAT Gateways