Mastering AWS VPC NAT Gateways


Why you need a NAT Gateway (in one minute)
In a well‑designed VPC, application servers often live in private subnets with no public IPs. They still need to reach the internet for package updates, pulling container images, or talking to third‑party APIs. A NAT Gateway lets instances in private subnets initiate outbound connections to the public internet without being directly reachable from it.
TL;DR: NAT Gateway = outbound‑only internet access for private subnets (IPv4). For IPv6, use an egress‑only internet gateway instead.
NAT Gateway vs Alternatives
NAT Gateway (managed, zonal): Highly available within one AZ, scales automatically, minimal ops. Billed per hour + per GB processed. Deploy one per AZ for HA and to avoid cross‑AZ data charges.
NAT Instance (self‑managed): Cheaper in tiny labs, but you patch, scale, secure, and failover it yourself.
VPC Endpoints (Gateway/Interface): Bypass the internet entirely for AWS services like S3/DynamoDB and often reduce NAT data costs.
Egress‑Only Internet Gateway: For IPv6 outbound‑only traffic.
What you’ll build
A VPC with two AZs, each containing a public and a private subnet. Each public subnet hosts a NAT Gateway; each private subnet routes default traffic to the NAT Gateway in the same AZ.
Architecture Diagram (Mermaid)
flowchart TB
Internet((Internet))
IGW[Internet Gateway]
subgraph VPC
direction TB
subgraph AZ-a
direction TB
PubA[Public Subnet (AZ-a)]
NATa[NAT Gateway (AZ-a)]
PrivA[Private Subnet (AZ-a)]
end
subgraph AZ-b
direction TB
PubB[Public Subnet (AZ-b)]
NATb[NAT Gateway (AZ-b)]
PrivB[Private Subnet (AZ-b)]
end
end
Internet --> IGW
IGW --> PubA
IGW --> PubB
PubA --> NATa
PubB --> NATb
PrivA --> NATa
PrivB --> NATb
Quick ASCII Diagram (fallback)
Internet
|
[IGW]
/ \
[PubA] [PubB]
| |
[NATa] [NATb]
| |
[PrivA] [PrivB] -> Instances have no public IPs; outbound goes via NAT in same AZ
Prerequisites
A VPC with at least two AZs (for HA).
Two public subnets (auto‑assign public IPv4 on), two private subnets (no public IPs).
An Internet Gateway (IGW) attached to the VPC.
Public route table(s) with default route
0.0.0.0/0 → IGW
.
Not strictly required but recommended: enable DNS hostnames and DNS resolution on the VPC for package mirrors and domain lookups.
Step‑by‑Step: Create NAT Gateways (AWS Console)
Allocate Elastic IPs (EIPs)
VPC Console → Elastic IP addresses → Allocate Elastic IP address.
Allocate one EIP per NAT Gateway (typically one per AZ).
Create NAT Gateways
VPC Console → NAT Gateways → Create NAT gateway.
Subnet: choose the public subnet in AZ‑a; attach EIP‑1; Connectivity type: Public.
Repeat for AZ‑b (public subnet in AZ‑b with EIP‑2).
Private Route Tables → Default Routes
VPC Console → Route tables → create (or edit) a route table for Private‑AZ‑a.
- Add route:
0.0.0.0/0 → NAT Gateway (AZ‑a)
.
- Add route:
Do the same for Private‑AZ‑b with its AZ‑b NAT Gateway.
(Optional IPv6) Add
::/0 → Egress‑only Internet Gateway
if you use IPv6.
Associate Route Tables to Private Subnets
Associate Private‑AZ‑a route table to Private Subnet in AZ‑a.
Associate Private‑AZ‑b route table to Private Subnet in AZ‑b.
Security Groups & NACLs
Security Groups: For instances in private subnets, ensure egress allows needed destinations (
0.0.0.0/0
is common; restrict if you can). Inbound is not required for outbound‑only.Network ACLs: Keep default stateless allows, or ensure ephemeral return ports (1024‑65535) are permitted for outbound flows.
Test Connectivity
Launch an EC2 in a private subnet without public IP.
Connect via SSM Session Manager (preferred) or via a bastion in the public subnet.
Run
curl
https://ifconfig.me
(should show the EIP of the NAT in that AZ), orsudo yum -y update
/apt update
to confirm outbound.
High Availability Notes
- A NAT Gateway is an AZ‑scoped resource. For HA and to avoid cross‑AZ data charges, place one NAT per AZ and ensure private subnets in that AZ route to the local NAT.
Cost & Optimization Tips
Charged per hour and per GB processed. Use VPC Endpoints (S3, DynamoDB, ECR, CloudWatch) to lower NAT traffic.
Turn off labs you’re not using. One NAT per AZ can be significant in non‑prod.
Optional: Terraform snippet
Use this as a starting template; adapt IDs and CIDRs to your environment.
# Elastic IPs (one per NAT)
resource "aws_eip" "nat_a" { domain = "vpc" }
resource "aws_eip" "nat_b" { domain = "vpc" }
# NAT Gateways in public subnets
resource "aws_nat_gateway" "a" {
allocation_id = aws_eip.nat_a.id
subnet_id = aws_subnet.public_a.id
tags = { Name = "nat-a" }
}
resource "aws_nat_gateway" "b" {
allocation_id = aws_eip.nat_b.id
subnet_id = aws_subnet.public_b.id
tags = { Name = "nat-b" }
}
# Private route tables
resource "aws_route_table" "priv_a" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.a.id
}
tags = { Name = "rt-private-a" }
}
resource "aws_route_table" "priv_b" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.b.id
}
tags = { Name = "rt-private-b" }
}
# Associate route tables with private subnets
resource "aws_route_table_association" "priv_a" {
subnet_id = aws_subnet.private_a.id
route_table_id = aws_route_table.priv_a.id
}
resource "aws_route_table_association" "priv_b" {
subnet_id = aws_subnet.private_b.id
route_table_id = aws_route_table.priv_b.id
}
Troubleshooting checklist
No internet from private instances?
Does the private route table have
0.0.0.0/0 → NAT
?Is the private subnet associated with that route table?
Is the NAT in a public subnet with a route to the IGW and an Elastic IP attached?
Security group egress allows outbound? NACLs allow ephemeral return ports?
Using IPv6? Remember NAT Gateway is IPv4‑only → use egress‑only IGW.
High data charges? Add VPC Endpoints for S3, DynamoDB, ECR/CloudWatch, or consider keeping NAT per AZ to avoid cross‑AZ charges.
Clean‑up (avoid charges)
Terminate test EC2 instances.
Delete route entries pointing to NAT, then delete route tables if dedicated.
Delete NAT Gateways.
Release Elastic IPs.
(If lab only) Detach & delete the IGW, subnets, and the VPC.
Summary
A NAT Gateway is the simplest, production‑ready way to provide outbound internet to private subnets. Keep it one per AZ, push heavy AWS‑service traffic to VPC Endpoints, and verify routes/associations carefully. With the steps above and the diagram, you can go from zero to a secure, internet‑enabled private tier in minutes.
Subscribe to my newsletter
Read articles from saumya singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

saumya singh
saumya singh
Welcome to my corner of the cloud, where ideas scale faster than servers and downtime is not an option! Here, I write about everything from spinning up VPCs to tearing down myths about the cloud. Whether you’re an engineer, a curious learner, or someone who just likes seeing words like 'serverless' and 'auto-scaling,' you’re in the right place. Consider this blog your high-availability zone for tips, tutorials, and tech thoughts—delivered with 99.99% uptime .