AWS NAT Gateway – Your Definitive Guide
If you’re working with AWS at all, you’ll eventually run into NAT Gateway. It’s one of those fundamental pieces that shows up in almost every basic VPC setup, so it helps to actually understand how it works.
This post covers what you need to know about AWS NAT Gateway.
What Changed Recently (2024-2026)
A few things shifted in the NAT Gateway landscape:
- NAT Gateway pricing held steady at ~$0.045/GB data processed + ~$0.045/hour (roughly $32/month idle). AWS introduced hourly discounts for accounts with Reserved Instance or Savings Plan commitments.
- PrivateLink for AWS services became the preferred approach over NAT Gateway + IGW for accessing AWS APIs. Interface VPC Endpoints are free and faster — no data transfer charges and no traversing the public internet.
- Transit Gateway adoption grew significantly. Instead of deploying per-VPC NAT Gateways, many organizations now route all egress through a central Transit Gateway with a shared NAT instance or gateway. Saves money at scale.
- VPC IP Address Manager (IPAM) integration — NAT Gateway integrates with IPAM for easier CIDR block management in multi-VPC architectures.
- CloudWatch metrics for NAT Gateway expanded to include
PeakPacketsPerSecondandPeakConnectionsPerSecond, making capacity planning more precise. - IPv6 egress — NAT Gateways do NOT support IPv6. For IPv6 outbound traffic, use an Egress-only Internet Gateway (free, per-VPC, no hourly cost, no data processing charge).
What is AWS NAT Gateway?
NAT stands for Network Address Translation. A NAT Gateway lets instances in a Private Subnet connect out to the internet. The classic use case: you’ve got servers that can’t have public IPs (maybe for security reasons), but they still need to download packages, pull OS updates, that sort of thing.
One thing to know upfront: external connections from the internet can’t reach instances through a NAT Gateway. The traffic only goes one way, outbound. That’s intentional and actually makes things simpler from a security perspective. More on this later.
Why use AWS NAT Gateway?
Here’s the typical scenario. You’ve got a database server. Does it really need to be in a Public Subnet? Probably not. It makes more sense to put it in a Private Subnet where only internal VPC traffic can reach it. But here’s the catch: that database server still needs to grab OS updates and maybe pull some packages during setup. Without a way out, you’re stuck.
That’s where the NAT Gateway comes in. It handles that outbound connectivity while keeping your servers unreachable from the outside.
AWS NAT Gateway: Connectivity Types
When you create a NAT Gateway in the AWS Console, you’ll pick a connectivity type: Public or Private. This trips up a lot of people, so let me break it down.
Public NAT Gateway
This is the default and most common option. It gives instances in a Private Subnet a way out to the internet. You associate an Elastic IP with it, and traffic from your private instances appears to come from that public IP.
Private NAT Gateway
The private connectivity type exists mainly for hybrid networks. I’m talking about setups where you need to connect your AWS resources to your on-premises data center.
Why use a Private NAT Gateway?
A few scenarios where this makes sense:
First, you might need instances in a Private Subnet to talk to other VPCs. Second, you might need to connect those instances to your on-premises servers.
The key to making this work is your Route Table. You configure routes from your Subnet through the NAT Gateway to either Transit Gateway or a Virtual Private Gateway. That’s what makes the whole thing tick.
Specific Use Cases
The private NAT Gateway solves some interesting problems. Here’s one that comes up more than you’d expect:
Let’s say you’ve got two AWS accounts, each with a VPC. You want to peer them. But wait — both VPCs are using the same IP range. That’s a problem. VPC Peering doesn’t support overlapping CIDR blocks.
What do you do? You can use a Private NAT Gateway with Transit Gateway to route traffic between them. Here’s the basic idea:

What are the differences between Public and Private NAT Gateway?
Both types handle IP translation, but they work differently:
Public NAT Gateway
- Uses an Elastic IP (a public IPv4 address)
- Traffic routes through an Internet Gateway
- AWS calls it “public” because you deploy it in the same Subnet as the Internet Gateway route
Private NAT Gateway
- No Elastic IP
- Takes an IP from your Subnet’s address range
- Can’t route traffic to an Internet Gateway
- Works with Transit Gateway or Virtual Private Gateway instead
How does NAT Gateway Work?
The mechanics are the same whether you’re using public or private connectivity. Here’s what happens:
Any time traffic leaves your instance, the NAT Gateway swaps out the source IP address. For private instances using a public NAT Gateway, the source IP becomes the Elastic IP. For private instances using a private NAT Gateway, it becomes the private IP from your subnet.
This actually makes life easier when you’re managing security groups. If you’ve got a bunch of backend servers sharing the same NAT Gateway, your database only sees one source IP. You can whitelist that single IP instead of maintaining a list of every server IP.
Here’s how it looks in practice:

Mapping NAT Gateway on Route Table
The Route Table above belongs to a Private Subnet with CIDR 10.170.0.0/22.
Any traffic destined for 0.0.0.0/0 (which means “everything not in this subnet”) gets routed to the NAT Gateway automatically.

How much does a NAT Gateway cost?
This is worth knowing, especially if you’re running things at scale. The pricing has two parts:
- An hourly charge while the NAT Gateway is provisioned
- Data transfer fees for traffic leaving the Availability Zone
Prices vary by region. In us-east-1 (Northern Virginia), you’re looking at around $0.045 per hour for the gateway itself. Data transfer runs about $0.045 per GB (for traffic leaving the AZ).
Note: AWS pricing changes regularly. Always check the official AWS pricing page for the latest numbers.
How to save money
Here’s a tip: you don’t pay for data transfer between EC2 and S3 if they’re in the same region.
So what’s the trick for avoiding cross-AZ charges? Deploy a NAT Gateway in each Availability Zone where you have servers that need one. This way, traffic stays within the same AZ.
Here’s a rough cost comparison worth knowing:
| Option | Hourly Cost | Data Transfer |
|---|---|---|
| NAT Gateway (us-east-1) | ~$0.045/hr (~$32/mo idle) | $0.045/GB |
| NAT Instance (t3.micro) | ~$0.0084/hr + EC2 cost | Included in instance cost |
For under 100 GB/month of data transfer, a NAT Instance is often cheaper once you factor in the NAT Gateway hourly fee. Above that threshold, NAT Gateway’s simplicity and managed SLA start winning.
NAT Gateway – Best Practices
Deploying NAT Gateways across Availability Zones isn’t just about saving on transfer fees. It’s also about keeping your setup resilient.
A NAT Gateway is highly available within its own zone. But here’s a scenario you want to avoid: sharing a single NAT Gateway across multiple Availability Zones.
Think about it. If that Availability Zone goes down, all your instances in other zones lose their internet access. Your “high availability” setup just became a single point of failure.
Here’s the multi-AZ pattern:
# Deploy one NAT Gateway per AZ — the right way
# AZ-1: private subnet routes to nat-01
# AZ-2: private subnet routes to nat-02
# If AZ-1 goes down, only AZ-1 instances lose egress — AZ-2 stays up
For multi-VPC architectures, consider Transit Gateway as a central egress point instead of per-VPC NAT Gateways. One NAT Gateway (or NAT instance) at the hub handles all VPCs, which is simpler and cheaper when you have many VPCs.
Use PrivateLink Instead of NAT Gateway for AWS Services
Some AWS services don’t need internet access at all. S3, DynamoDB, API Gateway, Secret Manager — these can all be reached through AWS’s internal network using VPC Endpoints.
New AWS users often stumble on this. They assume that since an instance is in AWS, it should automatically reach these services. But that’s not how it works. Even traffic between AWS services needs either internet access or a VPC Endpoint.
Using a NAT Gateway just to fetch secrets from Secrets Manager feels like overkill. Plus, you’re routing sensitive traffic through the internet, which isn’t ideal. VPC Endpoints (PrivateLink) use the AWS network backbone instead — they’re free and faster.
IPv6 Egress: Use Egress-only Internet Gateway
If you need IPv6 outbound connectivity, NAT Gateway doesn’t help — it only handles IPv4. For IPv6, use an Egress-only Internet Gateway instead:
# Terraform: Egress-only Internet Gateway for IPv6
resource "aws_egress_only_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
resource "aws_route_table" "private_ipv6" {
vpc_id = aws_vpc.main.id
route {
ipv6_cidr_block = "::/0"
egress_only_gateway_id = aws_egress_only_internet_gateway.main.id
}
}
Egress-only Internet Gateways are free — no hourly charge, no data processing fee.
Working with NAT Gateway via CLI
Here are the commands you’ll reach for most often:
# Create a NAT Gateway
aws ec2 create-nat-gateway \
--subnet-id subnet-0123456789abcdef0 \
--connectivity-type public \
--allocation-id eip-0123456789abcdef0
# Tag it for tracking
aws ec2 create-tags \
--resources nat-0123456789abcdef0 \
--tags Key=Name,Value=prod-natgw Key=Environment,Value=production
# Check NAT Gateway status
aws ec2 describe-nat-gateways \
--nat-gateway-ids nat-0123456789abcdef0
# Monitor with CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/NATGateway \
--metric-name BytesOutFromDestinationsThroughNatGateway \
--dimensions Name=NatGatewayId,Value=nat-0123456789abcdef0 \
--start-time 2026-04-01T00:00:00Z \
--end-time 2026-04-05T00:00:00Z \
--period 3600 \
--statistics Sum
# Delete a NAT Gateway
aws ec2 delete-nat-gateway \
--nat-gateway-id nat-0123456789abcdef0
Terraform for NAT Gateway
# Terraform: NAT Gateway with Route Table
resource "aws_eip" "nat" {
domain = "vpc"
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public.id
tags = {
Name = "prod-nat"
}
# NAT Gateways take 1-5 minutes to become active after creation
provisioner "local-exec" {
command = "echo 'NAT Gateway ${self.id} created — allow 1-5 minutes for activation'"
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
}
Limitations
A few things to know before you commit to NAT Gateway:
- Maximum 5 NAT Gateways per Availability Zone per account. Plan your AZ coverage accordingly.
- Bandwidth ranges from 5 Gbps to 100 Gbps and scales automatically based on usage. You don’t pick the size.
- 55,000 concurrent connections per EIP per destination. If you hit this limit, you’ll see
ErrorPortAllocationin CloudWatch. Use multiple NAT Gateways or implement connection pooling to work around it. - You can’t change the IP address after creation. Need a different IP? Create a new NAT Gateway and update your route tables.
- No Security Groups attach directly to NAT Gateways. You control traffic through the Security Groups on your instances instead.
- Port address translation limitations: NAT Gateway supports up to 55,000 concurrent connections per EIP. Exceeding this causes
ErrorPortAllocation.
Common Gotchas and Pitfalls
Here’s what bites people in practice:
Single NAT Gateway is a single point of failure. If you use one NAT Gateway for all AZs and it fails, all private subnets lose egress. Always deploy one NAT Gateway per AZ.
Port allocation errors at scale. The 55,000 concurrent connections per EIP limit trips up anyone running a high-volume proxy or many parallel workers behind a single NAT Gateway. Split traffic across multiple NAT Gateways in different AZs.
NAT Gateway is not accessible from the internet. It’s a fully managed service — you cannot SSH into it or troubleshoot it directly. For debugging connectivity, deploy a bastion host in the public subnet, or use VPC Flow Logs (which work on the NAT Gateway’s ENI).
Cross-AZ data transfer charges add up fast. If your private subnet in AZ-1 routes to a NAT Gateway in AZ-2, you’re paying $0.01/GB for cross-AZ traffic. Deploy NAT Gateways in each AZ and route locally.
Warm-up time. NAT Gateways can take 1-5 minutes to become active after creation. Don’t alarm on brief connection failures immediately after provisioning — check the NAT Gateway status with describe-nat-gateways first.
You cannot selectively block IPs. Unlike NAT Instances (where you can run iptables rules), you cannot block specific IPs at a NAT Gateway. For granular traffic filtering, you’ll need to use Security Groups on the instances behind the NAT Gateway.
VPC Flow Logs don’t capture NAT Gateway traffic by default. The NAT Gateway is a managed service, not a standard ENI. Use CloudWatch metrics (BytesOutFromDestinationsThroughNatGateway, ErrorPortAllocation, PacketsDropped) for visibility into NAT Gateway traffic patterns.
Testing NAT Gateway Connectivity
Once you’ve got everything set up, how do you verify it’s working?
Easy connectivity checks:
# Check your outbound IP (should match your Elastic IP)
curl -4 ifconfig.co
# Verify routing from within a private subnet instance
# SSH into your private instance via bastion, then:
curl -v https://ifconfig.co/ip
# Check DNS resolution works (for package updates)
nslookup amazon.com
# Monitor connection metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/NATGateway \
--metric-name ActiveConnectionCount \
--dimensions Name=NatGatewayId,Value=nat-0123456789abcdef0 \
--start-time 2026-04-05T00:00:00Z \
--end-time 2026-04-05T12:00:00Z \
--period 300 \
--statistics Average
When to Use NAT Gateway vs. Alternatives
| Scenario | Recommended Approach |
|---|---|
| Private instance needs internet access for updates | NAT Gateway (public) |
| Connecting VPCs with overlapping CIDRs | Private NAT Gateway + Transit Gateway |
| Accessing S3, DynamoDB, Secrets Manager | VPC Gateway Endpoints (free) |
| Accessing other AWS services via PrivateLink | VPC Interface Endpoints (free) |
| IPv6 outbound traffic | Egress-only Internet Gateway (free) |
| Low-traffic dev/test environments | NAT Instance (cheaper below ~100GB/mo) |
| Many VPCs needing shared egress | Transit Gateway + central NAT |
The landscape shifted meaningfully over 2024-2026: PrivateLink for AWS service access replaced NAT Gateway for most AWS API calls, and Transit Gateway became the standard for multi-VPC egress. If you’re building a new VPC from scratch, start with VPC Endpoints first — most private instances don’t need a NAT Gateway at all.
Transit Gateway: Centralized Egress at Scale
When you have multiple VPCs — say a dozen or more — deploying a NAT Gateway in each one gets expensive and hard to manage. AWS Transit Gateway acts as a hub that all your VPCs connect to. You put a single NAT Gateway (or NAT instance) at the Transit Gateway level and all VPCs share it for internet egress.
This has real advantages:
- Cost: One NAT Gateway instead of one per VPC
- Simplicity: Single set of routes to manage
- Visibility: All egress flows through one chokepoint where you can monitor it
The tradeoff: all egress traffic between VPCs and the NAT Gateway travels over the Transit Gateway backbone, which has its own data transfer charges. Run the numbers against per-VPC NAT Gateways for your specific traffic patterns.
# Terraform: Transit Gateway with shared NAT egress
resource "aws_ec2_transit_gateway" "main" {
description = "Central transit gateway"
default_route_table_propagation_vlas = "disable"
auto_accept_shared_attachments = "enable"
}
resource "aws_ec2_transit_gateway_vpc_attachment" "vpcs" {
count = 2
transit_gateway_id = aws_ec2_transit_gateway.main.id
vpc_id = element(["vpc-01", "vpc-02"], count.index)
subnet_ids = element([["subnet-a", "subnet-b"], ["subnet-c", "subnet-d"]], count.index)
}
# Route table: send all internet traffic (0.0.0.0/0) to NAT GW via TGW
resource "aws_vpc_route_table" "tgw_egress" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
transit_gateway_id = aws_ec2_transit_gateway.main.id
}
}
Setting Up CloudWatch Alarms for NAT Gateway
A production NAT Gateway setup isn’t complete without monitoring. Here are the key metrics to alarm on:
# Alarm: ErrorPortAllocation (connection limit approaching)
aws cloudwatch put-metric-alarm \
--alarm-name "natgw-port-exhaustion" \
--namespace AWS/NATGateway \
--metric-name ErrorPortAllocation \
--dimensions Name=NatGatewayId,Value=nat-0123456789abcdef0 \
--threshold 1000 \
--comparison-operator GreaterThanThreshold \
--period 300 \
--evaluation-periods 2 \
--statistic Sum \
--alarm-actions arn:aws:sns:us-east-1:123456789012:alerts
# Alarm: PacketsDropped (general health issue)
aws cloudwatch put-metric-alarm \
--alarm-name "natgw-packets-dropped" \
--namespace AWS/NATGateway \
--metric-name PacketsDropped \
--dimensions Name=NatGatewayId,Value=nat-0123456789abcdef0 \
--threshold 100 \
--comparison-operator GreaterThanThreshold \
--period 60 \
--evaluation-periods 3 \
--statistic Sum \
--alarm-actions arn:aws:sns:us-east-1:123456789012:alerts
# Alarm: Idle NAT Gateway (unexpected cost)
# A NAT Gateway at near-zero traffic is either expected or a misconfiguration
aws cloudwatch put-metric-alarm \
--alarm-name "natgw-high-bytes" \
--namespace AWS/NATGateway \
--metric-name BytesOutFromDestinationsThroughNatGateway \
--dimensions Name=NatGatewayId,Value=nat-0123456789abcdef0 \
--threshold 1073741824000 \
--comparison-operator GreaterThanThreshold \
--period 86400 \
--evaluation-periods 1 \
--statistic Sum \
--alarm-actions arn:aws:sns:us-east-1:123456789012:alerts
The ErrorPortAllocation alarm is the most important one — it tells you you’re about to hit the 55,000-connection ceiling before connections start failing. A proactive alert here gives you time to split traffic across additional NAT Gateways.
FAQ
Q: Can I share one NAT Gateway across multiple VPCs? Yes, but it’s not recommended. All cross-VPC traffic to the NAT Gateway crosses Availability Zones and incurs cross-AZ data transfer charges ($0.01/GB). More importantly, if the AZ where the NAT Gateway lives goes down, all VPCs lose egress. Use Transit Gateway with per-AZ NAT Gateways for multi-VPC egress instead.
Q: Why is my NAT Gateway showing high BytesInToDestination but low BytesOutFromDestination?
This usually means your instances are receiving traffic from the internet — which shouldn’t happen if the NAT Gateway is set up correctly (NAT Gateway only handles outbound traffic). Check your route tables: your private subnets might have an unintended route pointing inbound traffic to the NAT Gateway. Also verify Security Groups aren’t allowing inbound traffic from unknown sources.
Q: Does NAT Gateway support UDP? Yes, NAT Gateway handles TCP, UDP, and ICMP traffic in both directions (for responses to outbound requests). The 55,000 concurrent connection limit applies across all protocols.
Q: How do I migrate from a NAT Instance to a NAT Gateway?
Create the NAT Gateway in the same subnet as the NAT Instance (or the same AZ). Update the route tables to point 0.0.0.0/0 to the new NAT Gateway. Test connectivity from a private instance. Once confirmed working, terminate the NAT Instance. The transition should be seamless as long as the Elastic IP is the same.
Q: Can I use NAT Gateway for VPC-to-VPC communication? No — NAT Gateway is for internet-facing egress. For VPC-to-VPC traffic, use VPC Peering, Transit Gateway, or VPC Endpoints. If two VPCs have overlapping CIDRs, use Private NAT Gateway with Transit Gateway to handle the translation.
Q: What’s the difference between NAT Gateway and Internet Gateway? An Internet Gateway (IGW) allows instances with public IPs (EIPs or auto-assigned public IPs) to communicate with the internet directly — both inbound and outbound. A NAT Gateway allows instances with no public IPs to communicate outbound only. They’re complementary: the IGW handles inbound/outbound for public instances, the NAT Gateway handles outbound-only for private instances.
For more on AWS networking, the CloudFront vs Global Accelerator guide covers the CDN layer, and AWS STS covers IAM role-based access for VPC resources. The FinOps guide covers VPC cost optimization including NAT Gateway pricing strategies.
Comments