AWS NAT Gateway

By Vishvesh Patel · DevOps EngineerUpdated September 12, 2026

What a NAT gateway does, why it is often the largest networking cost on an AWS bill, and the VPC endpoints and architecture changes that reduce it.

A NAT gateway lets resources in a private subnet reach the internet outbound while remaining unreachable inbound. It is a managed, highly available service with no instances to patch.

It is also, on a great many AWS bills, the single largest networking line item, and usually the one nobody can immediately explain.

What it does

Private subnets have no route to an Internet Gateway, which is what makes them private. But the workloads in them still need to download packages, call third-party APIs, pull container images and reach AWS service endpoints.

The NAT gateway sits in a public subnet and translates outbound traffic from private subnets to its own public address. Responses come back; unsolicited inbound connections do not get in.

private subnet ──0.0.0.0/0──► NAT gateway ──► Internet Gateway ──► internet
  (10.0.32.0/20)              (in public subnet)

Two placement details people get wrong:

  • The NAT gateway goes in a public subnet, not a private one. Putting it in a private subnet gives it no path out and it simply does not work.
  • The route goes in the private subnet's route table, pointing 0.0.0.0/0 at the NAT gateway.
aws ec2 create-nat-gateway \
  --subnet-id subnet-public-a \
  --allocation-id eipalloc-0abc123

aws ec2 create-route \
  --route-table-id rtb-private-a \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id nat-0abc123

The cost model

A NAT gateway charges two ways, and the second is the one that gets out of hand:

ChargeNote
Hourly, per gatewayRuns continuously whether or not traffic flows
Per GB processedCharged on all traffic through it, in addition to normal data transfer

That per-GB processing charge applies to traffic that stays entirely inside AWS. A workload in a private subnet pulling container images from ECR, writing to CloudWatch Logs, or reading from S3 is paying a per-gigabyte NAT charge on traffic that never touches the internet.

And for high availability you want one gateway per Availability Zone, because a NAT gateway is zonal, if its AZ fails, private subnets routed to it lose outbound access. Three AZs means three hourly charges before a byte moves.

Reducing it

1. VPC endpoints, which is the big one

Gateway endpoints for S3 and DynamoDB are free. They add a route so traffic to those services goes directly rather than through NAT:

aws ec2 create-vpc-endpoint \
  --vpc-id vpc-0abc123 \
  --service-name com.amazonaws.eu-west-1.s3 \
  --route-table-ids rtb-private-a rtb-private-b

There is no reason not to create these. They cost nothing, take one command, and often remove a large share of NAT volume, including ECR image pulls, because image layers are stored in S3.

Interface endpoints (PrivateLink) cover most other AWS services and carry their own hourly and per-GB charges. They pay off when they displace meaningful NAT volume; the usual candidates are:

EndpointWhy
ecr.api and ecr.dkrContainer image pulls, constantly
logsCloudWatch Logs ingestion
secretsmanager, ssmFrequent small calls
stsRole assumption

Note that an interface endpoint is charged per AZ it is deployed into, so the arithmetic is worth doing rather than creating them reflexively.

2. Check what is actually flowing

Do not guess. Flow logs will tell you:

aws ec2 create-flow-logs \
  --resource-type VPC --resource-ids vpc-0abc123 \
  --traffic-type ALL \
  --log-destination-type s3 \
  --log-destination arn:aws:s3:::acme-flow-logs

Query with Athena, grouping by destination, and the top talkers are usually obvious, and usually AWS services that an endpoint would handle for free or cheaper.

The NatGatewayAddressCount and BytesOutToDestination CloudWatch metrics give a quicker top-level view.

3. Fewer gateways in non-production

One NAT gateway shared by all AZs costs a third as much and loses AZ redundancy. That is a reasonable trade in development and a bad one in production.

Be aware of the second-order effect: private subnets in other AZs then send traffic cross-AZ to reach it, which incurs cross-AZ data transfer charges. At high volume that can erode the saving.

4. Question whether you need it at all

Some workloads in private subnets have no genuine need for outbound internet access once VPC endpoints cover the AWS services they use. Removing the route removes the charge entirely, and tightens security, since the workload can no longer reach arbitrary destinations.

NAT gateway or NAT instance?

A NAT instance is an EC2 instance doing the same job with software.

NAT gatewayNAT instance
ManagementFully managedYou patch and monitor it
AvailabilityRedundant within its AZSingle instance unless you build failover
BandwidthScales automatically to high throughputBounded by the instance type
CostHourly + per GBInstance cost only
Security groupsNot applicableApplies

For production, use the NAT gateway. The managed availability and automatic scaling are worth the price, and a self-managed NAT instance becomes a single point of failure that someone has to maintain.

A NAT instance can genuinely be cheaper for a low-traffic development environment, since a small instance has no per-GB charge, but it is an operational item to own, so weigh that against the saving.

Troubleshooting

Private instances cannot reach the internet. Work through:

  1. Does the private subnet's route table have 0.0.0.0/0 → the NAT gateway?
  2. Is the NAT gateway in a public subnet, whose route table points 0.0.0.0/0 at an Internet Gateway?
  3. Does the NAT gateway have an Elastic IP and show state available?
  4. Does the instance's security group allow the outbound traffic?
  5. If NACLs are customised, do they allow outbound and the inbound ephemeral range?
aws ec2 describe-nat-gateways --filter Name=vpc-id,Values=vpc-0abc123 \
  --query 'NatGateways[].{ID:NatGatewayId,State:State,Subnet:SubnetId}'

ErrorPortAllocation in metrics means the gateway has exhausted its ports to a single destination. A NAT gateway supports a large but finite number of simultaneous connections per destination IP, and a workload opening very many connections to one endpoint can hit it. Spreading across multiple destinations, or using an interface endpoint for that service, resolves it.

Quick reference

TaskCommand
List gatewaysaws ec2 describe-nat-gateways --filter Name=vpc-id,Values=<vpc>
Create oneaws ec2 create-nat-gateway --subnet-id <public-subnet> --allocation-id <eip>
Route private trafficaws ec2 create-route --route-table-id <rtb> --destination-cidr-block 0.0.0.0/0 --nat-gateway-id <nat>
Free S3 endpointaws ec2 create-vpc-endpoint --vpc-id <vpc> --service-name com.amazonaws.<region>.s3 --route-table-ids <rtb>
Interface endpointaws ec2 create-vpc-endpoint --vpc-id <vpc> --vpc-endpoint-type Interface --service-name <svc> --subnet-ids <subnets>
Enable flow logsaws ec2 create-flow-logs --resource-type VPC --resource-ids <vpc> --traffic-type ALL
Deleteaws ec2 delete-nat-gateway --nat-gateway-id <nat>

Key takeaways

  • The NAT gateway lives in a public subnet; the route to it lives in the private subnet's route table.
  • It charges hourly and per gigabyte processed, including for traffic that never leaves AWS.
  • Gateway endpoints for S3 and DynamoDB are free and often remove a large share of NAT volume.
  • Interface endpoints for ECR, CloudWatch Logs and SSM usually cost less than the NAT traffic they displace.
  • One gateway per AZ for production; consolidating in dev saves money but adds cross-AZ transfer charges.
  • Measure with flow logs before optimising. The top talkers are rarely what people assume.
  • ErrorPortAllocation means port exhaustion to one destination, not a general capacity problem.

Check your understanding

Exercise

A NAT gateway that does nothing

A NAT gateway is created in a private subnet and the other private subnets route 0.0.0.0/0 to it. Instances still cannot reach the internet. Why?

Exercise

Paying to reach S3

A workload in private subnets writes large volumes to S3 and the NAT data processing charge is high. What is the most cost-effective change?


Frequently Asked Questions

What does a NAT gateway actually do?

It allows resources in private subnets to initiate outbound connections to the internet while remaining unreachable from it. Outbound traffic is translated to the gateway's public address, responses return to the originating instance, and unsolicited inbound connections are not possible. This is how workloads without public IPs download packages, call external APIs and reach AWS service endpoints while staying private.

Why is my NAT gateway so expensive?

Two charges combine: an hourly rate per gateway, which applies continuously, and a per-gigabyte data processing charge on everything that passes through, including traffic to AWS services that never leaves the AWS network. Since high availability means one gateway per Availability Zone, a three-AZ setup pays three hourly charges before any data moves. The per-gigabyte component is usually what makes the bill surprising, and it is almost always dominated by traffic to S3, ECR and CloudWatch Logs.

How do I reduce NAT gateway costs?

Add VPC endpoints. Gateway endpoints for S3 and DynamoDB are completely free and remove that traffic from NAT, which typically covers a large share. Container image pulls included, since ECR layers live in S3. Interface endpoints for ECR, CloudWatch Logs, SSM and STS carry their own charges but usually cost less than the NAT volume they replace. Enable flow logs first so you optimise against real traffic rather than assumptions, and consider consolidating to a single gateway in non-production.

Do I need a NAT gateway in every Availability Zone?

For production, yes. A NAT gateway is a zonal resource, so if its Availability Zone becomes unavailable, every private subnet routed to it loses outbound access, which turns a single-AZ problem into a multi-AZ outage. In development, a single shared gateway saves roughly two thirds of the hourly cost, at the price of that redundancy. Note that traffic from other AZs then crosses zones to reach it, incurring cross-AZ data transfer charges that partly offset the saving at volume.

What is the difference between a NAT gateway and a NAT instance?

A NAT gateway is fully managed, redundant within its Availability Zone, scales bandwidth automatically, and charges hourly plus per gigabyte. A NAT instance is an EC2 instance running NAT software, so you patch and monitor it, its throughput is bounded by the instance type, and it is a single point of failure unless you build failover yourself, but it has no per-gigabyte charge. Use a NAT gateway in production. A NAT instance can be cheaper for a low-traffic development environment if you accept owning it.

Can I use a NAT gateway for inbound traffic?

No, and that is the point of it. A NAT gateway only supports connections initiated from inside the VPC; it has no mechanism for forwarding unsolicited inbound connections to private instances. To accept inbound traffic, put a load balancer in a public subnet with targets in private subnets, which keeps the instances unreachable directly while still serving requests.

Continue Learning

Explore Related Topics

When it breaks

Related Resources