Networking: VPC, Route 53 & ELB
Verified against aws-cli/2.33.6, flags verified via `aws <cmd> help`, 2026-08-20 · official docs
VPC and subnet setup, routing and internet access, security group rules, Route 53 DNS records, and Elastic Load Balancing (ALB/NLB via elbv2).
VPCs and subnets#
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=my-vpc}]' aws ec2 describe-vpcs --filters "Name=tag:Name,Values=my-vpc" aws ec2 create-subnet --vpc-id vpc-0123456789abcdef0 --cidr-block 10.0.1.0/24 --availability-zone us-east-1a aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-0123456789abcdef0"
Internet access — gateway, route table, association#
aws ec2 create-internet-gateway aws ec2 attach-internet-gateway --internet-gateway-id igw-0123456789abcdef0 --vpc-id vpc-0123456789abcdef0 aws ec2 create-route-table --vpc-id vpc-0123456789abcdef0 aws ec2 create-route --route-table-id rtb-0123456789abcdef0 --destination-cidr-block 0.0.0.0/0 --gateway-id igw-0123456789abcdef0 aws ec2 associate-route-table --route-table-id rtb-0123456789abcdef0 --subnet-id subnet-0123456789abcdef0
A subnet is only "public" because its route table has a 0.0.0.0/0 route pointing at an internet gateway — there's no separate "make this subnet public" flag. This four-command chain (gateway → attach → route → associate) is the whole mechanism.
Security group rules#
aws ec2 authorize-security-group-ingress \ --group-id sg-0123456789abcdef0 \ --protocol tcp --port 443 --cidr 0.0.0.0/0 aws ec2 authorize-security-group-ingress \ --group-id sg-0123456789abcdef0 \ --protocol tcp --port 22 --source-group sg-0123456789abcdef1 # allow from another security group instead of a CIDR
Route 53 — hosted zones#
aws route53 list-hosted-zones aws route53 create-hosted-zone --name example.com --caller-reference "$(date +%s)"
--caller-reference must be a unique string per call (it's an idempotency token, not a DNS field) — a timestamp or UUID both work; a fixed literal will fail on a second run with the same name.
Route 53 — records#
aws route53 list-resource-record-sets --hosted-zone-id Z1234567890ABC aws route53 change-resource-record-sets --hosted-zone-id Z1234567890ABC --change-batch '{ "Changes": [{ "Action": "UPSERT", "ResourceRecordSet": { "Name": "app.example.com", "Type": "A", "TTL": 300, "ResourceRecords": [{"Value": "203.0.113.10"}] } }] }'
change-resource-record-sets is the only way to create/update/delete records — there's no create-record shortcut. UPSERT creates the record if it doesn't exist or overwrites it if it does; use CREATE/DELETE when you want the API to reject a call that doesn't match the record's current existence state.
Load balancers (ALB/NLB) — creation and listeners#
aws elbv2 create-load-balancer --name my-alb --subnets subnet-0123456789abcdef0 subnet-0123456789abcdef1 --security-groups sg-0123456789abcdef0 --type application aws elbv2 describe-load-balancers --names my-alb aws elbv2 create-target-group --name my-targets --protocol HTTP --port 80 --vpc-id vpc-0123456789abcdef0 --health-check-path /healthz aws elbv2 create-listener --load-balancer-arn <lb-arn> --protocol HTTP --port 80 --default-actions Type=forward,TargetGroupArn=<target-group-arn>
Target groups — registering and checking health#
aws elbv2 register-targets --target-group-arn <target-group-arn> --targets Id=i-0123456789abcdef0 aws elbv2 describe-target-health --target-group-arn <target-group-arn>
describe-target-health is the fastest way to confirm whether an ALB actually considers your instances/tasks healthy — a TargetHealth.State of unhealthy here, not application logs, is usually the first place to look when a load balancer is returning 502s.