Route S3 and DynamoDB traffic through free Gateway endpoints, put heavy AWS API calls behind Interface endpoints, and reserve the NAT Gateway for traffic that genuinely leaves AWS: that is the cost-correct split. A NAT Gateway bills by the hour and again for every gigabyte it processes, so AWS-to-AWS traffic routed through it pays for a detour it never needed. VPC endpoints keep that traffic on the AWS network, usually for far less, sometimes for nothing.
Why does the NAT Gateway line grow so fast?
A NAT Gateway has a double cost shape: a fixed hourly charge for existing, plus a data processing charge on every gigabyte that crosses it. The hourly part is predictable and small. The per-gigabyte part scales with traffic, which is why the line item grows with your workload rather than with your architecture.
Multiply that by one gateway per availability zone for high availability, and add the fact that the processing charge applies to traffic in both directions. The exact rates are on the official VPC pricing page; what matters here is the shape. Anything that moves serious volume through the NAT Gateway (backups, analytics reads, container image pulls) gets metered gigabyte by gigabyte.
The classic trap: private subnets calling S3 through NAT
The trap looks like this: a Lambda function or an ECS task in a private subnet reads from S3 or DynamoDB, and the route table sends that traffic through the NAT Gateway. Every gigabyte is metered, yet the destination is an AWS service that never required NAT in the first place.
It settles in quietly. A copied Terraform module wires private subnets to a NAT Gateway, workloads land in them, and S3 traffic follows the default route. Nothing breaks, latency stays fine, and the only symptom is a bill line. Data-heavy pipelines make it worse: an analytics job reading terabytes from S3 through NAT pays data processing on every byte, for zero functional benefit.
Which VPC endpoint replaces which traffic?
Gateway endpoints cover exactly two services, S3 and DynamoDB, and cost nothing: no hourly charge, no per-gigabyte charge, just an entry in your route tables. Interface endpoints (AWS PrivateLink) cover most other AWS APIs, with an hourly charge per endpoint per availability zone plus a per-gigabyte rate well below NAT data processing.
A Gateway endpoint takes minutes to add and deserves no debate: for S3 and DynamoDB it should be the default in every VPC. Interface endpoints deserve a quick calculation. For chatty or heavy services (ECR, CloudWatch Logs, SQS, Secrets Manager) the per-gigabyte savings dwarf the hourly cost; for a service you call rarely, the hourly charge per AZ can exceed what NAT would have billed. Check the volume before adding one per service.
Wondering how much of your NAT Gateway bill is just S3 traffic? Describe your system: a one-page diagnosis within 48 hours.
Get my diagnosis →NAT Gateway vs VPC endpoints: side by side
The decision is not either-or. A well-built production VPC typically ends up with Gateway endpoints for S3 and DynamoDB, a short list of Interface endpoints for its heaviest AWS APIs, and one NAT path kept for genuine internet egress. Here is where each option wins.
| NAT Gateway | Gateway endpoint | Interface endpoint | |
|---|---|---|---|
| What traffic | Anything outbound: internet, third-party APIs, AWS public endpoints | S3 and DynamoDB only | Most AWS APIs (ECR, SQS, CloudWatch, Secrets Manager...) |
| Cost shape | Hourly per gateway + per-GB processed | Free | Hourly per endpoint per AZ + per-GB, below the NAT rate |
| Setup effort | One gateway per AZ, default routes | One route table entry | One ENI per AZ per service, private DNS |
| When it wins | True internet egress with no AWS alternative | Always, for those two services | Sustained AWS API volume, or private-connectivity requirements |
When does the NAT Gateway stay necessary?
Keep a NAT Gateway when private workloads genuinely need the internet: third-party APIs, payment providers, outbound webhooks, OS package mirrors, external SaaS. VPC endpoints only reach AWS services and the PrivateLink offers a vendor explicitly publishes, so this traffic has no endpoint alternative.
The goal is to shrink the NAT Gateway, not to delete it. Once endpoints absorb the AWS-bound traffic, what remains is usually a thin stream of API calls, and the per-gigabyte charge stops mattering. Some teams also keep NAT for AWS services that lack endpoint support; that list shrinks every year, so it is worth rechecking before accepting it.
Do your Lambdas even need a VPC?
Often, no. A Lambda function needs VPC attachment only to reach resources that live inside the VPC: RDS, ElastiCache, internal load balancers. A function that talks only to S3, DynamoDB, and external HTTPS APIs runs without any VPC configuration and reaches all of them directly: no route tables, no NAT, no endpoints.
We regularly see functions attached to a VPC "for security" when nothing they call lives there, inheriting a NAT dependency and paying for it on every invocation's traffic. The Lambda VPC documentation is explicit that attachment exists to reach private resources; it is not a hardening step. Before buying endpoints for a Lambda-heavy stack, ask this prior question, and see our notes on Lambda best practices for the rest of the checklist.
How do you find the damage in Cost Explorer?
Open Cost Explorer, group by usage type, and look for NatGateway-Bytes: that is the data processing meter. Its sibling NatGateway-Hours is the fixed part. When Bytes dwarfs Hours, your money is going into traffic, and endpoints are usually the fix.
Two things hide the signal. NAT charges sit under the "EC2-Other" service in Cost Explorer, so a service-level view buries them. And the usage type tells you how much, not what: enable VPC Flow Logs on the NAT Gateway's network interface and check whether the top destinations match the S3 or DynamoDB prefix lists. If they do, the fix is a free route table entry. This is the first check we run in an architecture review.
Decision checklist
- Private subnets reaching S3 or DynamoDB? Add Gateway endpoints now: free, done in minutes.
NatGateway-BytesdominatingNatGateway-Hours? Trace the top destinations with VPC Flow Logs.- Heavy AWS API traffic (ECR, CloudWatch Logs, SQS)? Price an Interface endpoint against the per-gigabyte NAT charge.
- Lambdas in a VPC with no VPC-only dependency? Detach them.
- Real internet egress left over? Keep the NAT Gateway for that, deliberately sized.