The Silent Cost of NAT Gateways: Why You Should Use 3 VPC Endpoints for ECR & S3
Why using NAT Gateways for AWS-internal traffic is a costly architectural mistake, and how deploying 3 specific VPC Endpoints changes the pricing model entirely.
Source: LinkedIn post by Muhammad Basit
Managing complex cloud environments and deploying containerized applications at scale often means dealing with unexpected billing surprises. If you are running worker nodes in private subnets, one of the most notorious "silent killers" on an AWS bill is the NAT Gateway data processing charge.
Let's look at a very common scenario: worker EC2 instances in a private VPC subnet that need to pull container images from Elastic Container Registry (ECR) and read datasets from S3.
Why NAT Gateway is an Anti-Pattern Here
By default, instances in a private subnet route their outbound internet traffic through a NAT Gateway. While NAT Gateways are necessary for general external internet access (like downloading OS patches from third-party repositories), using them for heavy AWS-internal traffic is a costly architectural anti-pattern.
Here is the underlying pricing problem: NAT Gateways charge $0.045 per GB for data processed.
When your worker nodes pull Docker images or read objects from S3, that data is routed from your private subnet, out through the NAT Gateway, and then to the public endpoints of S3 and ECR. If you are constantly pulling heavy image layers or processing data pipelines, every single byte is metered. You are essentially paying a premium data processing fee for traffic that technically shouldn't even need to leave the AWS network backbone.
The Alternative: Replacing 1 NAT Gateway Flow with 3 VPC Endpoints
To fix this, we don't necessarily delete the NAT Gateway (you might still need it for external APIs), but we completely offload the S3 and ECR traffic from it. Based on the "After" architecture in the diagram, the solution is to deploy 3 specific VPC Endpoints.
VPC Endpoints allow you to privately connect your VPC to supported AWS services. The traffic remains entirely within the AWS backbone, bypassing the NAT Gateway and its hefty data processing fees.
Here is the breakdown of the 3 endpoints you need to provision:
1. S3 Gateway Endpoint (1 Endpoint)
First, we create an S3 Gateway Endpoint. The pricing for this is simple: $0. Gateway Endpoints are completely free. You simply add a route to your VPC route table directing all S3-bound traffic to this endpoint. This handles the bulk of the data transfer.
2. ECR Interface Endpoints (2 Endpoints)
Next, we set up VPC Interface Endpoints (powered by AWS PrivateLink) for ECR. You specifically need two of them to make ECR work privately:
com.amazonaws.<region>.ecr.api(For ECR API calls)com.amazonaws.<region>.ecr.dkr(For Docker client commands)
Interface endpoints are not free, but their pricing model is much more forgiving. You pay a small hourly rate per Availability Zone, plus a significantly lower data processing fee (around $0.01 per GB) compared to the NAT Gateway.
The Secret Sauce: How They Work Together
You might ask: "If I am pulling huge Docker images, won't I still pay data processing fees on the two ECR Interface Endpoints?"
This is where the architecture shines: Docker image layers in ECR actually live in S3.
When a worker pulls an image, the initial API requests and Docker authentication go through the 2 ECR Interface Endpoints. However, the actual heavy lifting—the gigabytes of image layers—is downloaded directly from S3. Because we already set up the free S3 Gateway Endpoint, those massive layer downloads route through the free endpoint.
The Pricing Shift
By making this architectural shift, you fundamentally change the cost dynamic of your infrastructure:
- Before: You pay $0.045 for every gigabyte of S3 and ECR traffic going through the NAT Gateway.
- After: You pay $0 for all S3 data (including the heavy ECR layers), and only a small hourly fee + a minor processing fee for the lightweight API traffic hitting the interface endpoints.
Originally shared on LinkedIn. The architecture diagram above is taken from that post.