May 8, 2026 7 min read
The S3 Gateway Endpoint nobody enabled (and the $40k NAT bill it explains)
A Gateway Endpoint for S3 is free. It costs zero hourly, zero per GB, and routes private-subnet S3 traffic around NAT entirely. It is also the single most-missed line in the AWS audit playbook. The pattern, the math on a real account we audited, and the one Terraform resource that fixes it.
Two years ago we onboarded a SaaS company with a $40,000 monthly NAT Gateway bill. The engineering team was halfway through a PrivateLink rollout, expecting it to bring the line down. They were also about to spend three engineering weeks on it. The audit took two hours and the fix was one Terraform resource.
The problem was simpler than they thought. They had no Gateway Endpoint for S3. Every byte of S3 read and write traffic from their private subnets routed through NAT, paying the $0.045 per-GB data-processing fee on traffic that should have cost zero. The company moved roughly 800 TB per month to and from S3. Eight hundred terabytes at $0.045 per gigabyte is $36,864. That single line was 92 percent of the NAT bill.
What a Gateway Endpoint actually is
AWS sells two flavors of VPC Endpoint. Interface Endpoints, also called PrivateLink, attach an ENI to a subnet and bill $0.01 per AZ per hour plus $0.01 per GB. Gateway Endpoints attach to a route table and bill nothing. Zero hourly, zero per gigabyte. They exist for two services only: S3 and DynamoDB.
Mechanically, the Gateway Endpoint adds a prefix-list route to the route table that says "traffic destined for the S3 service prefix in this region goes through the endpoint". Once that route is in place, packets to S3 do not traverse the NAT Gateway. They go straight to the AWS backbone and back, never hitting the public internet, never billing the data-processing fee.
Why this gets missed
Three reasons, and we see all three across audits.
First, the AWS console default for a new VPC does not include a Gateway Endpoint. The VPC Wizard creates a NAT and assumes outbound traffic is the goal. Teams using the wizard get a working VPC, S3 traffic flows, the bill stays under the radar until the workload scales.
Second, the Terraform module ecosystem split on this. Some modules include S3 and DynamoDB Gateway Endpoints by default. Others ask you to opt in. Teams that pinned an older module version often miss the opt-in flag. The traffic flows either way, so nothing looks wrong from outside.
Third, the line item label is unhelpful. NAT Gateway data
processing shows up as NatGateway-Bytes on the bill,
not as "S3 traffic that should have been free". The correlation
between S3 read patterns and NAT growth requires Flow Logs or CUR
analysis, and most teams do not run that monthly.
The Terraform diff
For a single-region VPC with a private route table, the fix is one resource:
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.${var.region}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = aws_route_table.private[*].id
} Apply this and S3 traffic from any subnet associated with those route tables stops paying the NAT data-processing fee. There is no downtime, no redeploy, no application change. Existing connections finish on the old route, new connections take the new one. The bill responds within hours.
For DynamoDB, change com.amazonaws.{region}.s3
to com.amazonaws.{region}.dynamodb and apply
the same resource a second time.
How to find out if you are paying for this
Two checks, in order.
Check one: list your VPC Endpoints. If there is no
Gateway-type endpoint with service name ending in
.s3, you have not enabled it. aws ec2
describe-vpc-endpoints --filters Name=vpc-endpoint-type,Values=Gateway
returns the list.
Check two: filter your Cost and Usage Report on
UsageType matching NatGateway-Bytes. If
that number is more than 30 percent of your total NAT line, S3 and
DynamoDB traffic almost certainly explains the rest. Confirm with
VPC Flow Logs: filter on destination IPs in the S3 prefix list,
sum bytes, and compare to the NAT total.
For a quick estimate without Flow Logs, the VPC Endpoint cost calculator shows what your current traffic would cost on endpoints versus NAT. The Gateway Endpoint column reads zero, which is the point.
What other services should be on Interface Endpoints
Gateway Endpoints only cover S3 and DynamoDB. For everything else, Interface Endpoints are the path. The high-leverage ones in most accounts are:
- ECR (image pulls). Container starts pull image layers from ECR, and a busy cluster can move tens of gigabytes per hour.
- SSM and EC2 Messages (Session Manager). Every shell session and every Run Command goes through these.
- Logs (CloudWatch Logs ingestion). Every log line your apps emit to CloudWatch is bytes through this.
- Secrets Manager and KMS. Low volume but high frequency, and the per-AZ hourly fee is small enough to be worth it.
Interface Endpoints are not free. They run roughly $7.30 per AZ per month plus $0.01 per GB. The break-even versus NAT data-processing is around 209 GB per month per AZ per service. Below that, NAT is cheaper for that service. Above that, the endpoint pays back fast.
What we tell every customer in the audit
Three rules, in order:
Rule one: enable the S3 and DynamoDB Gateway Endpoints. Always. Even if you do not use S3 today, enable it. The cost is zero and the day someone adds an S3 SDK call from a Lambda, you do not pay $0.045 per GB on the rollout.
Rule two: audit your Interface Endpoints quarterly against the 209 GB break-even. Services that have dropped below that volume should be removed and their traffic returned to NAT. We see accounts paying $50 a month per service for endpoints that move 20 GB.
Rule three: keep the endpoint AZ count matched to the workload AZ count. Three AZs of compute, three AZs of endpoint. Two AZs of compute, two AZs of endpoint. The third AZ on a two-AZ workload is $7.30 a month per service for nothing.
The first rule is the one that returns five-figure savings on day one. The other two are the polish that keeps the bill steady.
Keep reading
More from the blog
May 8, 2026 · 9 min read
Cross-AZ data transfer: the quiet tax on every chatty AWS workload
Cross-AZ data transfer charges $0.01 per GB in each direction. That sounds like nothing until you see what a chatty microservice mesh, a multi-AZ RDS, and a misplaced NAT Gateway can do to it. The patterns we see, and the architectural fixes that pay back in weeks.
May 8, 2026 · 10 min read
Transit Gateway: the hub-and-spoke tax on multi-VPC AWS
Transit Gateway looks cheap on paper. $0.05 per attachment per hour, $0.02 per GB processed. Then a 12-VPC hub-and-spoke quietly costs $1,300 a month, and the cross-AZ surcharge hides on a different bill line entirely. The patterns and the four moves to bring it down.