Find what flows through your NAT gateways, then move it to free or cheaper paths. Prices, break-even math, and a command for each fix.
By Evgeny Anikiev, AWS-certified DevOps engineer with 10+ years running build automation, CI/CD, and Kubernetes in production. Prices verified October 22, 2026, against AWS public pricing pages (US East list prices; check your Region).
TL;DR: A NAT gateway charges $0.045 per hour per AZ plus $0.045 for every GB it processes, including traffic to S3, ECR, and other AWS services. Most of that traffic doesn't need a NAT at all. Measure what flows through it first, then move AWS-bound traffic to VPC endpoints and keep the rest in the same AZ.
The seven fixes, roughly in order of savings:
- Add free S3 and DynamoDB gateway endpoints.
- Add interface endpoints for high-volume AWS APIs (ECR, CloudWatch Logs, STS).
- Stop sending traffic across AZs to reach a NAT gateway.
- Keep Kubernetes service traffic in-zone.
- Send IPv6 egress through an egress-only internet gateway.
- Release idle public IPv4 addresses.
- Show each team its own NAT and data transfer bill.
What is an AWS NAT gateway?
An AWS NAT gateway is a managed service that lets resources in private subnets open connections to the internet and to public AWS service endpoints, while blocking connections coming in from outside. AWS bills it by the hour and by every GB it processes, which is why it shows up on so many networking bills.
How much does an AWS NAT gateway cost?
An AWS NAT gateway costs $0.045 per NAT gateway-hour and $0.045 per GB of data processed, at the US East list price AWS uses in its own pricing example. Partial hours bill as full hours, and the per-GB charge applies "regardless of the traffic's source or destination." Internet egress is billed on top.
The rates that matter for this playbook (US East, list price):
- NAT gateway (zonal): $0.045 per hour, about $32.85 per month per gateway.
- NAT gateway (regional mode): $0.045 per hour per AZ it runs in. Three AZs means three NAT gateway-hours every hour.
- NAT data processing: $0.045 per GB, both directions, any destination.
- Internet data transfer out: $0.09 per GB in the first tier, after 100 GB free per month across your account.
- Gateway VPC endpoint (S3, DynamoDB): no hourly or data processing charge.
- Interface VPC endpoint (PrivateLink): $0.01 per hour per AZ, plus $0.01 per GB processed.
- Public IPv4 address: $0.005 per hour, in use or idle (about $3.65 per month).
- Cross-AZ traffic inside a Region: $0.01 per GB, charged on each side of the transfer.
The processing fee is where the money goes. A node pulling a 1 GB container image from ECR through a NAT gateway pays $0.045 for that pull. Multiply that by hundreds of nodes scaling up and down every day. That traffic never needed to leave AWS.
How do I find what is using my NAT gateway?
Start with two numbers: how much each NAT gateway processes, and where those bytes go.
Cost Explorer. Filter Service = EC2-Other and group by Usage Type. Look for NatGateway-Bytes, NatGateway-Hours, and DataTransfer-Regional-Bytes (cross-AZ).
Cost and Usage Report (CUR) in Athena. Rank NAT gateways by cost:
SELECT line_item_resource_id,
line_item_usage_type,
ROUND(SUM(line_item_unblended_cost), 2) AS cost
FROM cur
WHERE line_item_usage_type LIKE '%NatGateway%'
AND line_item_usage_start_date >= date_add('day', -30, current_date)
GROUP BY 1, 2
ORDER BY cost DESC
LIMIT 20;
VPC Flow Logs on the NAT gateway ENI. This tells you where the bytes go. Enable flow logs on the NAT's network interface, query them in Athena, and rank destinations:
SELECT dstaddr,
ROUND(SUM(bytes) / 1e9, 1) AS gb
FROM vpc_flow_logs
WHERE interface_id = 'eni-0123456789abcdef0'
AND day >= '2026/10/01'
GROUP BY dstaddr
ORDER BY gb DESC
LIMIT 25;
Match the top destination IPs against AWS's published ranges to see which ones are AWS services:
curl -s https://ip-ranges.amazonaws.com/ip-ranges.json \
| jq -r '.prefixes[] | select(.region=="us-east-1") | "\(.ip_prefix) \(.service)"' \
> aws-ranges.txt
On container platforms, expect S3 (container image layers, artifacts, logs), ECR, CloudWatch Logs, and STS near the top. All of those can bypass the NAT.
How to reduce NAT gateway costs: seven fixes
1. Add S3 and DynamoDB gateway endpoints (free)
What it does: A gateway endpoint adds a route so traffic to S3 or DynamoDB stays on the AWS network and skips the NAT. AWS charges nothing for it, hourly or per GB.
Why it matters for containers: ECR stores image layers in S3. Without an S3 gateway endpoint, every layer download pays the NAT processing fee.
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--service-name com.amazonaws.us-east-1.s3 \
--vpc-endpoint-type Gateway \
--route-table-ids rtb-0aaa rtb-0bbb rtb-0ccc
Attach it to every private route table, then repeat with com.amazonaws.us-east-1.dynamodb if you use DynamoDB. It costs nothing and needs no application changes, so ship it first.
2. Add interface endpoints for high-volume AWS APIs
What it does: An interface endpoint (PrivateLink) puts a private IP for an AWS service inside your subnets. Traffic costs $0.01 per GB at the endpoint instead of $0.045 per GB at the NAT.
Break-even: Each endpoint costs about $7.30 per AZ per month. You save $0.035 per GB moved off the NAT, so an endpoint pays for itself at roughly 210 GB per month per AZ. For a three-AZ VPC, that is about 630 GB per month per service.
Good candidates for container platforms:
-
ecr.apiandecr.dkr(image manifests and auth; layers go through S3) -
logs(CloudWatch Logs, often the noisiest) -
sts(IRSA and EKS Pod Identity token calls) -
sqs,kinesis-streams,secretsmanager, depending on your workload
Use your flow log ranking from the queries above to decide. Don't create endpoints for services that move a few GB a month; they will cost more than they save.
3. Stop sending traffic across AZs to reach a NAT gateway
What it does: If private subnets in AZ b and AZ c route to a single NAT gateway in AZ a, every byte pays cross-AZ transfer on top of NAT processing.
The math: Two extra zonal NAT gateways cost about $65.70 per month. Cross-AZ traffic costs $0.02 per GB counting both sides. Above roughly 3.3 TB per month of cross-AZ NAT traffic, one NAT per AZ is cheaper, and it removes a single point of failure too.
Check your route tables:
aws ec2 describe-route-tables \
--filters Name=vpc-id,Values=vpc-0123456789abcdef0 \
--query 'RouteTables[].{rt:RouteTableId,subnets:Associations[].SubnetId,nat:Routes[?NatGatewayId].NatGatewayId}' \
--output json
Each private subnet's route table should point at the NAT gateway in the same AZ.
Or use a regional NAT gateway. Since November 2025, a NAT gateway can run in regional availability mode: one gateway that expands into each AZ where you have workloads, with no public subnet per AZ and no per-AZ route table edits. It bills per AZ-hour, so it costs the same as one zonal NAT per AZ. AWS's launch post lists two caveats. Expansion into a new AZ takes 15 to 20 minutes on average, and up to 60. Until it finishes, traffic from that AZ goes to another AZ's NAT and pays cross-AZ charges.
4. Keep Kubernetes service traffic in-zone
Cross-AZ charges don't only come from NAT routing. Service-to-service traffic in EKS crosses AZs all the time because kube-proxy spreads connections across all endpoints by default.
On Kubernetes 1.33 and later, ask for zone-local routing per Service:
apiVersion: v1
kind: Service
metadata:
name: orders
spec:
trafficDistribution: PreferClose
selector:
app: orders
ports:
- port: 80
targetPort: 8080
PreferClose sends traffic to endpoints in the client's zone when they exist and falls back to others when they don't. Pair it with topology spread constraints so each zone has replicas to receive the traffic. This traffic never touches the NAT, so it won't show up in your NAT numbers. Size it with the DataTransfer-Regional-Bytes usage type in Cost Explorer. If you're also planning an upgrade, see my EKS 1.37 upgrade checklist.
5. Send IPv6 egress through an egress-only internet gateway
An egress-only internet gateway lets IPv6 workloads reach the internet without a NAT gateway, so there is no NAT hourly or processing charge on that traffic. Standard data transfer out still applies. If your EKS cluster runs in IPv6 mode, or your dependencies (package registries, SaaS APIs) support IPv6, route ::/0 to an egress-only internet gateway and keep the NAT for IPv4-only destinations.
6. Release idle public IPv4 addresses
Every public IPv4 address costs $0.005 per hour whether it is in use or not. Find Elastic IPs that aren't attached to anything:
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==null].[PublicIp,AllocationId,Tags]' \
--output table
VPC IPAM's Public IP Insights view shows every public IPv4 in the account, including ones attached to load balancers and instances you may have forgotten.
7. Show each team its own network bill
Platform teams own the VPC, so NAT and data transfer costs usually land in one shared bucket that nobody feels responsible for. Split it:
- Tag NAT gateways and endpoints with the owning team or environment and activate those tags as cost allocation tags.
- Group flow logs by source subnet (or by pod CIDR in EKS) to attribute NAT bytes to the team that generated them.
- Publish a monthly showback: GB through NAT per team, top destinations, and which fix from this list would remove it.
A monthly report line such as "your service pulled 4 TB of images through the NAT; an S3 gateway endpoint makes that free" gives a team a specific action to take.
Worked example: an EKS cluster across three AZs
This is an illustrative model using the list prices above, not a client bill. Assume three zonal NAT gateways process 20 TB (20,480 GB) per month:
- 15 TB to S3 (image layers, artifacts)
- 2 TB to CloudWatch Logs
- 3 TB to the internet
Before:
- NAT hours: 3 × $0.045 × 730 = $98.55
- NAT processing: 20,480 GB × $0.045 = $921.60
- Total NAT cost: about $1,020 per month
After fixes 1 and 2:
- S3 moves to a gateway endpoint: $0
- CloudWatch Logs moves to an interface endpoint: 2,048 GB × $0.01 + 3 AZs × $7.30 = $42.38
- NAT keeps only internet traffic: 3,072 GB × $0.045 + $98.55 = $236.79
- Total: about $279 per month, a 73% cut
Internet data transfer out (about $276 at $0.09 per GB) is the same in both cases; endpoints don't change egress pricing. Your traffic mix will differ, so run the flow log queries above to get your own split before estimating savings.
NAT gateway cost checklist (copy-paste)
- [ ] Cost Explorer:
NatGateway-BytesandDataTransfer-Regional-Bytesbaselined - [ ] Flow logs enabled on every NAT ENI; top 25 destinations identified
- [ ] S3 gateway endpoint attached to every private route table
- [ ] DynamoDB gateway endpoint attached (if used)
- [ ] Interface endpoints for AWS services above ~210 GB per AZ per month
- [ ] Each private subnet routes to a NAT in its own AZ (or regional NAT in automatic mode)
- [ ] Kubernetes Services with heavy east-west traffic use
trafficDistribution: PreferClose - [ ] IPv6 egress goes through an egress-only internet gateway
- [ ] No unattached Elastic IPs
- [ ] NAT gateways and endpoints tagged; monthly showback per team
FAQ
Why is my AWS NAT gateway bill so high?
Usually because AWS-bound traffic (S3, ECR image layers, CloudWatch Logs) goes through the NAT and pays $0.045 per GB in processing. Container platforms with frequent node scaling pull large images many times a day. Enable flow logs on the NAT ENI to confirm where the bytes go.
Does a NAT gateway charge for traffic to S3?
Yes, if there is no S3 gateway endpoint. The NAT processing fee applies to all traffic it handles, regardless of destination. An S3 gateway endpoint is free and removes that traffic from the NAT completely.
Is a regional NAT gateway cheaper than zonal NAT gateways?
Not on price. Regional mode bills $0.045 per hour for each AZ it runs in, the same as one zonal NAT per AZ, and the same per-GB fee. It saves operational work (no public subnet per AZ, no route table edits) and helps avoid cross-AZ routing once it has expanded into every AZ you use.
Should I replace NAT gateways with NAT instances?
For small or non-production environments, a NAT instance on a small Graviton instance (for example with the open-source fck-nat AMI) avoids the per-GB processing fee. You take on patching, scaling, and failover yourself. For production, fix the traffic first with endpoints; the remaining NAT bill is often small enough that the managed gateway is worth it.
VPC endpoint vs NAT gateway: which is cheaper for AWS service traffic?
A gateway endpoint (S3, DynamoDB) is always cheaper because it is free. An interface endpoint costs $0.01 per GB plus about $7.30 per AZ per month, against $0.045 per GB through a NAT. It pays for itself above roughly 210 GB per month per AZ for that service.
How much does cross-AZ data transfer cost on AWS?
Traffic between Availability Zones in the same Region is billed at $0.01 per GB on each side, so $0.02 per GB in total. It appears as DataTransfer-Regional-Bytes in Cost Explorer. Same-AZ NAT routing and zone-aware Kubernetes Services reduce it.
How much does a NAT gateway cost per month?
About $32.85 per month per zonal gateway in hourly charges at US East list price, before data. Add $0.045 for every GB processed. A three-AZ setup starts at about $98.55 per month before any traffic.
Related reading
- Migrating AI Agents to AWS: 15 Bedrock and AgentCore Checks: a readiness score for AI teams moving agents to Amazon Bedrock and AgentCore.
- EKS 1.37 Upgrade Checklist: What Breaks and How to Test It: seven changes that break EKS clusters going from 1.36 to 1.37, with a test command for each.
Sources
- Amazon VPC pricing (NAT gateway, regional NAT, gateway endpoints, public IPv4)
- AWS PrivateLink pricing
- Introducing Amazon VPC Regional NAT Gateway (AWS Networking blog, Nov 20, 2025)
- Amazon EC2 On-Demand pricing: data transfer
- Understanding data transfer costs for AWS container services (AWS Containers blog)
- AWS IP address ranges
Want a second pair of eyes on your AWS network bill? I run NAT and data transfer cost reviews for platform teams at DevOps Cloud Consult.













