EC2 No Internet Access in a Custom VPC: Attaching an IGW and Fixing Route Tables
TL;DR
Launching an EC2 instance in a custom VPC public subnet without internet access almost always comes down to three missing pieces. Fix all three and traffic flows.
| Missing Component | Symptom | Fix |
|---|---|---|
| Internet Gateway (IGW) | No outbound route exists | Create & attach IGW to VPC |
| Route Table Entry | 0.0.0.0/0 has no target | Add route: 0.0.0.0/0 → IGW |
| Public IP / Elastic IP | Instance has no routable address | Enable auto-assign public IP or attach EIP |
Why This Happens: The Architecture Logic
AWS does not wire internet connectivity automatically when you create a custom VPC. The default VPC comes pre-configured with an IGW and a default route, which is why beginners never hit this wall there. The moment you create a custom VPC, you own the entire network stack.
Think of it like a new office building. The building (VPC) has rooms (subnets) and internal hallways (local routes). But until the city connects a road to the building's front door (IGW) and you post a sign saying "all outbound traffic uses this road" (route table entry), no one inside can reach the outside world — regardless of how "public" the room label says it is.
Data Flow: Before vs. After the Fix
BEFORE (broken state):
EC2 Instance
└─► Subnet Route Table
├─ 10.0.0.0/16 → local ✅ (intra-VPC works)
└─ (no default route) ❌ (internet traffic dropped)
AFTER (working state):
EC2 Instance (with Public IP)
└─► Subnet Route Table
├─ 10.0.0.0/16 → local ✅
└─ 0.0.0.0/0 → igw-xxxxxxxx ✅
└─► Internet Gateway
└─► Public Internet (google.com)
Step-by-Step Fix: AWS CLI (Least Privilege, No Console)
Execute these commands in order. Replace placeholder values with your own resource IDs.
Step 1 — Create and Attach the Internet Gateway
# Create the IGW
aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=my-igw}]' \
--query 'InternetGateway.InternetGatewayId' \
--output text
# Output: igw-0abc123def456
# Attach it to your VPC
aws ec2 attach-internet-gateway \
--internet-gateway-id igw-0abc123def456 \
--vpc-id vpc-0123456789abcdef0
Step 2 — Add the Default Route to the Route Table
# Find the route table associated with your public subnet
aws ec2 describe-route-tables \
--filters "Name=association.subnet-id,Values=subnet-0abc123" \
--query 'RouteTables[*].RouteTableId' \
--output text
# Output: rtb-0abc123def
# Add the 0.0.0.0/0 route pointing to the IGW
aws ec2 create-route \
--route-table-id rtb-0abc123def \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-0abc123def456
Step 3 — Ensure the EC2 Instance Has a Public IP
# Option A: Allocate and associate an Elastic IP (recommended for production)
aws ec2 allocate-address --domain vpc
# Output: AllocationId = eipalloc-0abc123
aws ec2 associate-address \
--instance-id i-0abc123def456 \
--allocation-id eipalloc-0abc123
# Option B: Enable auto-assign public IP on the subnet (dev/test)
aws ec2 modify-subnet-attribute \
--subnet-id subnet-0abc123 \
--map-public-ip-on-launch
Step 4 — Verify Security Group Allows ICMP (for ping)
# Allow outbound ICMP (ping to google.com requires this)
aws ec2 authorize-security-group-egress \
--group-id sg-0abc123 \
--protocol icmp \
--port -1 \
--cidr 0.0.0.0/0
# If testing inbound ping TO the instance:
aws ec2 authorize-security-group-ingress \
--group-id sg-0abc123 \
--protocol icmp \
--port -1 \
--cidr 0.0.0.0/0
Terraform Equivalent (IaC-First Teams)
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = { Name = "main-igw" }
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = { Name = "public-rt" }
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
IAM: Minimum Required Permissions
Apply least-privilege. The IAM principal executing these commands needs only:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:CreateInternetGateway",
"ec2:AttachInternetGateway",
"ec2:DescribeRouteTables",
"ec2:CreateRoute",
"ec2:AllocateAddress",
"ec2:AssociateAddress",
"ec2:ModifySubnetAttribute",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateTags"
],
"Resource": "*"
}
]
}
Scope Resource to specific VPC ARNs in production to tighten this further.
Cost Impact
- Internet Gateway: No hourly charge for the IGW itself. You pay for data transfer out at ~$0.09/GB (varies by region). Inbound data is free.
- Elastic IP: Free while associated with a running instance. Charged at ~$0.005/hr when allocated but not associated — release unused EIPs immediately.
- NAT Gateway (not used here): If your use case later involves private subnets, a NAT Gateway costs ~$0.045/hr + data processing fees — a common cost surprise for teams scaling up.
Glossary
| Term | Definition |
|---|---|
| Internet Gateway (IGW) | A horizontally scaled, redundant VPC component that enables communication between instances in your VPC and the public internet. |
| Route Table | A set of rules (routes) that determine where network traffic from a subnet or gateway is directed. |
| Public Subnet | A subnet whose associated route table contains a route to an Internet Gateway, making resources reachable from the internet (given a public IP). |
| Elastic IP (EIP) | A static, public IPv4 address allocated to your AWS account that can be dynamically remapped to any instance in your VPC. |
| Security Group | A stateful virtual firewall controlling inbound and outbound traffic at the instance level using allow-only rules. |
Comments
Post a Comment