AWS for DevOps — Internet & NAT Gateways
4.3 — Internet & NAT Gateways
An Internet Gateway (IGW) connects your VPC to the internet. A NAT Gateway lets resources in private subnets access the internet without being directly reachable from outside.
Internet Gateway
Internet
│
▼
┌──────────────┐
│ Internet │ 1:1 mapping with a VPC
│ Gateway │ Highly available by design
│ (igw-abc123) │ No bandwidth limits
└──────┬───────┘
│
┌──────┴────── VPC ──────────────────────┐
│ Public Subnet │
│ Route: 0.0.0.0/0 → igw-abc123 │
│ ┌────────┐ │
│ │ EC2 │ ← has public IP │
│ └────────┘ │
└─────────────────────────────────────────┘
Creating an Internet Gateway
# Create the IGW
$ aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=prod-igw}]'
{
"InternetGateway": {
"InternetGatewayId": "igw-abc123"
}
}
# Attach it to the VPC
$ aws ec2 attach-internet-gateway \
--internet-gateway-id igw-abc123 \
--vpc-id vpc-0abc123
# Add a route in the public route table
$ aws ec2 create-route \
--route-table-id rtb-pub123 \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-abc123
For an instance in a public subnet to reach the internet, it needs:
- ✅ A route to the IGW in its route table
- ✅ A public IP (or Elastic IP)
- ✅ A security group that allows the traffic
NAT Gateway
Private subnets have no route to the IGW, so instances inside cannot reach the internet directly. But they often need to — for software updates, pulling Docker images, or calling external APIs.
A NAT Gateway solves this:
Internet
│
▼
┌──── IGW ────┐
│ │
│ ┌─────────┴───── Public Subnet ──────┐
│ │ NAT Gateway (nat-def456) │
│ │ Elastic IP: 52.66.100.200 │
│ └──────────────┬─────────────────────┘
│ │
│ ┌──────────────┴── Private Subnet ───┐
│ │ Route: 0.0.0.0/0 → nat-def456 │
│ │ ┌────────┐ ┌────────┐ │
│ │ │ EC2 │ │ ECS │ │
│ │ │ app-01 │ │ task │ │
│ │ └────────┘ └────────┘ │
│ └────────────────────────────────────┘
└────────────────────────────────────────┘
How it works:
- App server sends a request to
https://api.github.com - Traffic goes to NAT Gateway (via route table)
- NAT Gateway translates the private IP to its Elastic IP
- Request goes through the IGW to the internet
- Response comes back through IGW → NAT → app server
Key property: Outside servers see the NAT Gateway’s Elastic IP, not the app server’s private IP. The app server is not reachable from the internet.
Creating a NAT Gateway
# Step 1 — Allocate an Elastic IP for the NAT Gateway
$ aws ec2 allocate-address --domain vpc
{
"AllocationId": "eipalloc-nat123",
"PublicIp": "52.66.100.200"
}
# Step 2 — Create the NAT Gateway in a PUBLIC subnet
$ aws ec2 create-nat-gateway \
--subnet-id subnet-pub1a \
--allocation-id eipalloc-nat123 \
--tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=nat-1a}]'
{
"NatGateway": {
"NatGatewayId": "nat-def456",
"State": "pending"
}
}
# Wait for it to be available
$ aws ec2 wait nat-gateway-available --nat-gateway-ids nat-def456
# Step 3 — Add a route in the private route table
$ aws ec2 create-route \
--route-table-id rtb-prv123 \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-def456
High Availability for NAT
A single NAT Gateway lives in one AZ. If that AZ goes down, private subnets in other AZs lose internet access. For production, create one NAT Gateway per AZ:
AZ-a AZ-b
┌── Public Subnet ──┐ ┌── Public Subnet ──┐
│ NAT-a │ │ NAT-b │
└────────┬───────────┘ └────────┬───────────┘
│ │
┌── Private Subnet ─┐ ┌── Private Subnet ─┐
│ Route → NAT-a │ │ Route → NAT-b │
│ App servers │ │ App servers │
└────────────────────┘ └────────────────────┘
# NAT Gateway in AZ-b
$ aws ec2 allocate-address --domain vpc
$ aws ec2 create-nat-gateway \
--subnet-id subnet-pub1b \
--allocation-id eipalloc-nat456
# Separate route table for private subnets in AZ-b
$ aws ec2 create-route \
--route-table-id rtb-prv-1b \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-ghi789
NAT Gateway Costs
| Component | Cost (ap-south-1, approximate) |
|---|---|
| Hourly charge | ~$0.045/hour (~$33/month) |
| Data processing | ~$0.045/GB |
Cost tip: NAT Gateways are one of the biggest hidden costs on AWS. For dev/test, consider a NAT instance (a small EC2 running NAT) or use VPC endpoints to avoid NAT for AWS service calls.
IGW vs NAT Gateway Comparison
| Feature | Internet Gateway | NAT Gateway |
|---|---|---|
| Direction | Bidirectional (inbound + outbound) | Outbound only |
| Placed in | VPC level | Public subnet |
| Cost | Free | ~$33/month + data |
| Used by | Public subnet resources | Private subnet resources |
| Public IP needed | Yes (on the instance) | Yes (Elastic IP on NAT) |
| High availability | Built-in | One per AZ for HA |
Common Mistakes
| Mistake | Why It Hurts | Fix |
|---|---|---|
| Placing NAT Gateway in a private subnet | NAT itself can’t reach the internet | Always create NAT in a public subnet |
| Only one NAT Gateway for all AZs | AZ failure affects all private subnets | One NAT per AZ |
| Not realising NAT costs add up | Surprise bills | Monitor data processing, use VPC endpoints |
| Forgetting the Elastic IP for NAT | NAT creation fails | Allocate EIP first |
Quick Reference
| Task | Command |
|---|---|
| Create IGW | aws ec2 create-internet-gateway |
| Attach IGW | aws ec2 attach-internet-gateway --internet-gateway-id IGW --vpc-id VPC |
| Create NAT | aws ec2 create-nat-gateway --subnet-id SUBNET --allocation-id EIP |
| Route to IGW | aws ec2 create-route --route-table-id RT --destination-cidr-block 0.0.0.0/0 --gateway-id IGW |
| Route to NAT | aws ec2 create-route --route-table-id RT --destination-cidr-block 0.0.0.0/0 --nat-gateway-id NAT |
| Delete NAT | aws ec2 delete-nat-gateway --nat-gateway-id NAT |
Next → Security Groups vs NACLs