← Back to blog

AWS NAT Gateway Pricing: The Hidden Tax, and How to Kill It

awscost-optimizationvpcnetworking
AWS NAT Gateway Pricing: The Hidden Tax, and How to Kill It

If your AWS bill has a NAT Gateway line, you are paying twice for the same packet: once for the gateway to merely exist, and again for every gigabyte it carries. The fix for most teams is dull and free. Add an S3 and a DynamoDB gateway endpoint, route the heavy traffic away from NAT, and only then argue about anything fancier. That single change is free to turn on, takes minutes, and stops the most expensive traffic from ever touching the meter.

This is a playbook, not a lecture. The trick with NAT Gateway pricing is that the two charges hide in different places on the bill, so most teams only ever see half of it. Numbers first, then the fixes, in the order I would actually do them.

What you are actually being charged for

NAT Gateway has two charges, and people forget the second one until they read the bill closely.

  1. Hourly charge — you pay for every hour the gateway is provisioned and available, whether or not a single byte moves through it. In us-east-1 (N. Virginia) and us-east-2 (Ohio) this is $0.045 per NAT Gateway-hour. That is roughly $32.85 a month per gateway just to keep the lights on. Partial hours bill as full hours.

  2. Data processing charge — you pay $0.045 per GB processed through the gateway, in the same region, on top of the hourly charge. This applies to every gigabyte, inbound or outbound, regardless of source or destination.

And then there is the part the pricing page mentions almost in passing: standard AWS data transfer charges still apply on top. NAT processing is an extra meter on traffic you were already paying to move.

The hourly charge is fixed and visible. The per-GB charge is the one that catches teams out, because it scales with traffic you mostly cannot see: package installs, container image pulls, S3 reads from private subnets, telemetry shipped out, cross-region calls. The rate varies by region (it runs higher in places like São Paulo, where both the hourly and per-GB rates sit around $0.093), so check your own region rather than trusting the Ohio number. (AWS VPC Pricing; NAT Gateway pricing docs, accessed June 2026.)

Why it hides

Three reasons the NAT line stays invisible until it is large.

It is bundled with traffic you think is “free.” A private subnet pulling a Docker image from a public registry, or an app reading from S3 over the public S3 endpoint, looks like ordinary egress. Every one of those gigabytes is also a NAT data-processing gigabyte at $0.045.

It scales with success, not with headcount. You provision one NAT Gateway on day one and forget it. Traffic grows with users and deploys; the per-GB charge grows with it, silently, while the line item stays named the same boring thing.

Cross-AZ traffic gets routed through it the expensive way. If your workloads sit in one Availability Zone and your NAT Gateway sits in another, that traffic crosses an AZ boundary to reach NAT and crosses back — racking up inter-AZ data transfer charges in addition to NAT processing. AWS’s own guidance is explicit: keep resources in the same AZ as the NAT Gateway, or run a NAT Gateway in each AZ that has resources. (NAT Gateway pricing docs, accessed June 2026.)

How to spot it on your bill

Before you fix anything, measure. Fifteen minutes in the console tells you whether this is a $30 problem or a $3,000 one.

  • Cost Explorer — group by Usage Type and filter for NatGateway-Hours and NatGateway-Bytes. The first is your fixed cost (number of gateways × hours). The second is the one worth chasing.
  • VPC Flow Logs — turn them on for the NAT’d subnets and look at where the bytes go. If a large share of destinations are S3 or DynamoDB IP ranges, that is your easiest win: those bytes should never touch NAT.
  • The smell test — a single NAT Gateway carrying multiple TB a month, in an account doing heavy S3 or container work, almost always means you are paying NAT to do a job a free endpoint should be doing.

The fixes, in the order I would do them

1. Add S3 and DynamoDB gateway endpoints — free, do this today

This is the first move and it is not close. Gateway VPC endpoints for Amazon S3 and DynamoDB carry no hourly charge and no data-processing charge. AWS states it plainly: there are no data-processing or hourly charges for using Gateway-type VPC endpoints. (AWS VPC Pricing; Gateway endpoints docs, accessed June 2026.)

A gateway endpoint adds a route to your subnet’s route table so traffic to S3 or DynamoDB goes over a private AWS path instead of out through NAT. Every gigabyte you move to that path is a gigabyte that stops costing you $0.045 of NAT processing. If you run anything S3-heavy from private subnets — backups, data pipelines, log shipping, static asset reads — this one change can take a meaningful bite out of the bill for the price of a route-table edit.

There is no real downside. Create one in any VPC that talks to S3 or DynamoDB. The only catch worth knowing: gateway endpoints work from inside the VPC only — they do not serve on-premises networks, peered VPCs in other regions, or transit gateways. For those, you need the interface type below.

Gateway endpoints exist for exactly two services. Everything else — ECR (container image pulls), CloudWatch Logs, SSM, Secrets Manager, STS, Kinesis, and so on — uses interface endpoints, which run on AWS PrivateLink and are not free.

Interface endpoint pricing: $0.01 per hour per endpoint, per Availability Zone, plus $0.01 per GB processed (tiered down for very large volumes — $0.006/GB past 1 PB, $0.004/GB past 5 PB). (AWS PrivateLink pricing, accessed June 2026.)

So an interface endpoint is roughly $7.30 a month per AZ in hourly cost, plus a per-GB rate that is less than a quarter of NAT’s $0.045/GB. The decision is arithmetic:

  • High-volume service traffic (think ECR image pulls on every deploy, or chatty CloudWatch Logs) — the $0.01/GB endpoint beats the $0.045/GB NAT path easily, and you stop paying twice.
  • Low-volume, occasional traffic — the per-AZ hourly fee can cost more than just letting it ride over NAT. Do the math per service. A handful of MB a month does not justify $7.30 × number-of-AZs in standing charges.

The classic win here is ECR. Container-heavy accounts pull large images through NAT on every deploy and autoscale event; moving ECR (plus its S3 backing layer via the gateway endpoint, and CloudWatch Logs) onto endpoints is often where the NAT bill collapses.

3. Fix the cross-AZ routing

If your NAT Gateway lives in one AZ and your workloads in another, you are paying inter-AZ transfer on top of NAT processing. Either co-locate the workloads with the gateway, or run one NAT Gateway per AZ that has resources so traffic never crosses a boundary to reach it. Per-AZ NAT Gateways cost more in fixed hourly fees but can be cheaper overall once cross-AZ transfer is large — again, measure before deciding. (NAT Gateway pricing docs, accessed June 2026.)

4. Question whether you need NAT at all

A surprising number of private subnets only ever talk to AWS services and a short list of known external hosts. Once S3, DynamoDB, ECR, and your logging/secrets services are on endpoints, what is actually left going to the public internet? Sometimes the honest answer is “almost nothing,” and the NAT Gateway is $32.85/month of standing charge guarding a trickle. If a workload needs no inbound and only talks to AWS, it may not need NAT or even a public path at all.

5. Replace NAT with a NAT instance (fck-nat) — only with eyes open

For the cost-sensitive, the open-source pattern is fck-nat: a self-managed NAT instance on a small ARM box instead of the managed gateway. On a t4g.nano it runs at roughly $3–4 a month, supports up to ~5 Gbps of burst traffic (the EC2 egress cap), and — the real saving — charges no per-GB processing fee, because it is just an EC2 instance forwarding packets. Reported savings run up to ~90%+ versus managed NAT. (fck-nat project, accessed June 2026.)

But be honest about what you are buying. The managed NAT Gateway is fully managed and highly available within its AZ; fck-nat is an instance you own. By default it has no automatic failover — if the instance dies, that subnet’s egress dies until it recovers. You handle patching, monitoring, and scaling. The project’s own authors recommend the managed gateway for workloads that need five-nines uptime. (fck-nat project, accessed June 2026.)

So the rule I would apply: fck-nat for dev, staging, and internal/batch workloads where a few minutes of egress downtime is survivable. Managed NAT Gateway for production paths where it is not. This is the one fix on the list with a real operational cost; do not reach for it before you have done the free endpoints, which carry no trade-off at all.

The order is the point

The reason to do these in sequence is that the cheap, no-downside fixes capture most of the savings. Gateway endpoints are free and risk-free; do them first. Interface endpoints are cheap and a simple cost calculation; do them next, per service. Cross-AZ and architecture cleanups are free but need a little thought. fck-nat is the only one that trades money for operational responsibility, so it goes last and only where the trade is acceptable.

Most teams never need to get past step two. The S3 and DynamoDB endpoints alone are free, take minutes, and remove the single most common reason the NAT line creeps up. If you read only one sentence of this and act on it, make it that one.

NAT is usually the first place I look on a mid-size bill — the broader order I work an account in is what I’d audit first on a $50K AWS bill, and the S3 side of the same traffic problem is the S3 cost optimization playbook.

Sources