Where the cost comes from
Instances in a private subnet usually reach AWS services such as Amazon S3, Amazon ECR or AWS Systems Manager through a NAT gateway. A NAT gateway is billed for every hour it runs and for every gigabyte it processes, on top of normal data transfer charges (Amazon VPC pricing). For a workload that pulls container images, ships logs or reads large objects from S3, the per-gigabyte processing charge often grows larger than the hourly charge.

That traffic does not leave the AWS network even when it goes through a NAT gateway and internet gateway, as the AWS PrivateLink documentation notes. So the reason to add VPC endpoints is usually cost, routing control and access policy, not keeping traffic off the public internet.
There are two kinds of endpoint for AWS services, and they are priced very differently.
Gateway endpoints: free, for S3 and DynamoDB only
Gateway endpoints exist only for Amazon S3 and Amazon DynamoDB. They have no hourly or data processing charge, and the VPC pricing page states that you can use one to avoid NAT gateway processing charges for S3 traffic.
They do not use network interfaces, subnets or security groups. Instead, when you create one you choose route tables, and AWS adds a route whose destination is the AWS-managed prefix list for the service in that Region and whose target is the endpoint. Every subnet that uses one of those route tables then reaches the service through the endpoint, and because the prefix list route is more specific, it wins over a 0.0.0.0/0 route to a NAT gateway.
Creating an S3 gateway endpoint with the AWS CLI:
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc123example \
--service-name com.amazonaws.us-east-2.s3 \
--route-table-ids rtb-0abc123example rtb-0def456example
Things to know:
- Prefix lists are per Region, so traffic to an S3 bucket in another Region does not use the endpoint.
- Your security groups and network ACLs must still allow outbound HTTPS to the service. Security groups can reference the prefix list; network ACLs need the CIDR ranges.
- Gateway endpoints only serve traffic that starts in the VPC itself. For access from on-premises networks or other VPCs, use an interface endpoint for S3 instead.
For almost any VPC with private subnets that talk to S3 or DynamoDB, there is no reason not to add gateway endpoints.
Interface endpoints: billed, but often cheaper than NAT
Most other AWS services, such as Amazon ECR, AWS Systems Manager, AWS Secrets Manager, AWS Key Management Service, Amazon CloudWatch Logs and AWS STS, are reached through interface endpoints powered by AWS PrivateLink. Each one places an elastic network interface with a private IP address in the subnets you choose, one subnet per Availability Zone, and you attach a security group to it.
Interface endpoints are billed for each hour they are provisioned in each Availability Zone, plus a charge for each gigabyte processed (AWS PrivateLink pricing). That means each extra service and each extra Availability Zone adds a fixed monthly cost, whether or not traffic flows.
Whether an interface endpoint saves money depends on volume:
- The NAT gateway processing rate is higher than the interface endpoint processing rate. Check both on the pricing pages for your Region.
- Every endpoint adds an hourly charge per Availability Zone, so an endpoint for a service you barely use costs more than it saves.
- The NAT gateway itself does not go away if anything still needs the internet, so the saving is on processing charges only.
High-volume services such as Amazon ECR image pulls, CloudWatch Logs and Amazon Kinesis Data Streams are usually worth an endpoint. Low-volume control plane calls usually are not, unless you need private access for security or compliance reasons.
Pulling images from Amazon ECR privately needs the ecr.api and ecr.dkr interface endpoints plus the S3 gateway endpoint, because image layers are stored in S3.
Creating an interface endpoint with private DNS:
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc123example \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.us-east-2.logs \
--subnet-ids subnet-0aaa111example subnet-0bbb222example \
--security-group-ids sg-0ccc333example \
--private-dns-enabled
With private DNS turned on, and DNS hostnames and DNS resolution enabled on the VPC, the service’s normal Regional hostname resolves to the endpoint’s private IP addresses. Applications and SDKs need no changes. The endpoint’s security group must allow inbound HTTPS (TCP 443) from the clients that use it.
One cost trap: if you create an endpoint in only one Availability Zone, clients in other zones still use it, which adds cross-zone data transfer and leaves them without access if that zone is impaired. AWS recommends at least two Availability Zones for production.
Encryption is your client’s job
A VPC endpoint does not encrypt traffic for you. Requests to S3 or any other service are encrypted in transit because the client uses HTTPS, with or without an endpoint. If you need to enforce this, deny requests where aws:SecureTransport is false in your bucket policy.
Lock endpoints down with policies
Each endpoint can have an endpoint policy, an IAM resource policy that limits what can be done through it. The default policy allows full access. A tighter S3 gateway endpoint policy that allows only your own buckets looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::example-app-data",
"arn:aws:s3:::example-app-data/*"
]
}
]
}
An endpoint policy does not grant permissions on its own: the caller’s IAM policy and the bucket policy still apply. In the other direction, a bucket policy can use the aws:SourceVpce condition key to accept requests only through a specific endpoint. Check what else your workloads use before restricting S3: package repositories and some AWS services are served from S3 buckets you do not own, and a tight policy will break them.
A practical order of work
- Check the NAT gateway metrics in Amazon CloudWatch, such as
BytesOutToDestination, or group your Cost and Usage Report by usage type, to see where NAT processing charges come from. - Add S3 and DynamoDB gateway endpoints to every VPC with private subnets.
- Use VPC Flow Logs to find which other AWS services carry the most traffic through the NAT gateway.
- Add interface endpoints only where the volume justifies the hourly cost, in at least two Availability Zones.
- Tighten endpoint policies once traffic is flowing and you know what it needs.
9Acts is an AWS Advanced Tier Services Partner and AWS MSP Partner. If you want a review of your network costs alongside the rest of your bill, see our cloud optimization service.
