/storage_letter.

S3 VPC Endpoint Configuration for Private Bucket Access

Gateway endpoints eliminate NAT routing costs and latency ceilings for same-region S3 access.

Senior Editor · · 11 min read
Cover illustration for “S3 VPC Endpoint Configuration for Private Bucket Access”
Multi-Cloud Storage · September 10, 2026 · 11 min read · 2,523 words

EC2 instances don't talk to S3 directly by default. They go out through an internet gateway or a NAT gateway, hit S3's public endpoint, and come back, all while you pay for the privilege of routing your own traffic in a circle. Fixing that means choosing between two VPC endpoint types, gateway or interface, and that choice is the actual subject here, not just a cost line item.

The cost angle is a fine hook, though. NAT gateway pricing runs $0.045 per hour plus $0.045 per GB processed. There's no separate internet egress charge for same-region S3 traffic, so the real damage is that per-GB processing fee stacking on top of an hourly charge you're paying whether or not you send a single byte. Add up 35 network cost audits run across 2025 and 2026 by leanopstech.com, and the pattern holds: average bills run three to four times what they should, with NAT-routed S3 traffic showing up again and again as a repeat offender. A meaningful chunk of NAT traffic in a typical account, somewhere in the 40 to 60% range, is headed toward AWS services that already have free or cheaper endpoint options sitting right there, unused.

There's a security wrinkle too. Traffic routed through NAT and an internet gateway technically leaves the AWS network before it comes back, which means bucket policies that lean on source IP restrictions get shaky. You can't cleanly say "only my VPC can touch this bucket" without an endpoint in the picture. So the cost problem and the security problem point at the same fix. The question is which fix.

The two endpoint architectures and what separates them

A gateway endpoint isn't a piece of network hardware. It's a route table entry. No elastic network interface, no private IP address assigned to it, no hourly bill. You add it to your VPC, it modifies your routing, and traffic to S3 flows across the AWS network instead of bouncing out through NAT. It still uses S3's public IP ranges under the hood, but it never touches the public internet to get there. Free, and about as low-maintenance as AWS infrastructure gets.

An interface endpoint is a different animal entirely. This is AWS PrivateLink, and it works by provisioning actual elastic network interfaces, one per Availability Zone you select, each with a private IP address pulled from your own subnet. That gets billed: $0.01 per hour per AZ, plus $0.01 per GB processed. Spin one up across three AZs and you're looking at roughly $21.90 a month before a single gigabyte of data moves. It also changes how DNS resolution works. Unless you turn on private DNS, you need endpoint-specific hostnames to reach it, which is one more thing to configure and one more thing to get wrong.

Both keep your traffic off the public internet. That part's identical. The real difference is architecture and reach: what a gateway endpoint can't do, an interface endpoint can, and vice versa on the cost side. Neither one is the "secure" option and the other the "insecure" one. That's not the axis they differ on.

When a gateway endpoint is the right and complete answer

If your EC2 instances live in the same region as your S3 bucket, and nothing on-premises, no other region, and no transit gateway needs to reach that bucket, a gateway endpoint solves the whole problem. Full stop. No caveats needed.

Setup is almost anticlimactic. Create the endpoint in the VPC console, choose the service com.amazonaws.<region>.s3, and attach it to the route tables tied to your instance subnets. AWS drops in a route automatically, destination set to the S3 prefix list ID, target set to the endpoint. No DNS changes. No --endpoint-url flag in your CLI commands. It just works with the S3 addresses you're already using.

Security groups need a rule allowing traffic to and from S3, and you can reference the prefix list ID directly instead of typing out IP ranges by hand, which is a small mercy. Network ACLs are less forgiving: they can't reference prefix lists at all, so you're stuck pulling the actual IP ranges out of the list and entering them manually. There's a per-region cap of 20 gateway endpoints (adjustable on request) and a hard ceiling of 255 per VPC, neither of which most teams will ever bump into.

One operational trap worth flagging clearly: creating or modifying a gateway endpoint changes your VPC's routing and severs open TCP connections that were using the old public-address path. Those connections don't resume on their own. If your application can't reconnect automatically, don't make this change in the middle of a critical job. Schedule it like you would any routing change, because that's exactly what it is.

And a distinction that trips people up: the standard S3 gateway endpoint does not cover S3 Express One Zone traffic. That needs its own separate endpoint, com.amazonaws.<region>.s3express. Having one does not mean you have the other. Assuming otherwise is how teams end up debugging a "why is this still going through NAT" mystery for an afternoon.

The throughput case for the gateway endpoint: NAT as a performance ceiling, not just a cost problem

Cost isn't the only thing NAT gateway quietly taxes. Bandwidth is too, and for high-throughput workloads that ceiling shows up before you'd expect it. NAT gateway bandwidth starts around 5 Gbps and scales up toward 100 Gbps, but plenty of training and data pipelines are built to push far more traffic than that path can carry before something else in the stack becomes the bottleneck.

A throughput study on S3 Express One Zone, run through AWS re:Post, put numbers to this. Traffic routed through NAT flattened out around 25 Gbps, and past that point, adding more connections didn't add more throughput. It just made tail latency worse. The gateway endpoint path, tested against the same workload, kept climbing all the way to 55.7 Gbps at 64 connections, with no ceiling visible yet. Here's the detail that gives the whole thing away: at the NAT ceiling, instance CPU sat around 17%. The compute was nearly idle. The network path was the bottleneck, not the machine.

Latency told the same story from a different angle. On the NAT path, p99 latency climbed from about 345 milliseconds up past 1.2 seconds as connections piled up against the cap. On the gateway endpoint path, p99 stayed in a tight band, 255 to 326 milliseconds, across the entire range of connection counts tested. If you ever see throughput flatline while CPU sits low and idle, that's the signature.

This matters more for S3 Express One Zone specifically, since that storage class was built for machine learning training with single-digit-millisecond response times, up to 10 times faster than S3 Standard, and pricing that dropped in April 2025 (storage down 31%, GET requests down 85%). None of that speed means anything if your traffic is stuck behind a NAT gateway acting like a toll booth on a highway built for ten lanes. For AI and ML pipelines pushing serious throughput, the gateway endpoint isn't a nice-to-have. It's the precondition for getting what you're already paying for.

Worth noting separately: some filesystem layers that mount S3 directly and cache reads on local NVMe storage sidestep this NAT bottleneck by design, since the caching layer changes where the bottleneck could even form. Even then, how the underlying S3 traffic reaches AWS still depends on the endpoint configuration sitting beneath it.

Diagram: NAT vs. Gateway Endpoint: Throughput and Latency at the Ceiling. Visualizes: Show a side-by-side performance comparison between the NAT gateway path and the gateway endpoint path for S3 traffic, using the concrete numbers from the…

When a gateway endpoint is not enough: on-premises, cross-region, and transit gateway access

Gateway endpoints have three hard limits, and they're not edge cases. They're standard enterprise topology.

No access from on-premises networks over VPN or Direct Connect. No access from a VPC peered in from a different region. No access routed through a transit gateway. A gateway endpoint's reach stops at the edge of its own VPC, full stop, no exceptions. Anything sitting on the far side of a peering connection, a VPN tunnel, a Direct Connect link, or a transit gateway hub simply cannot use it to reach S3, no matter how the route tables are configured.

Hybrid environments with Direct Connect back to a data center. Multi-region architectures where a bucket in one region needs to serve compute in another. Hub-and-spoke transit gateway setups connecting a dozen VPCs to shared services. These aren't rare configurations dreamed up by unusually complicated companies. They're normal enterprise architecture, and all three need an interface endpoint instead.

Configuring an interface endpoint for on-premises and cross-region access

Setup starts the same way conceptually, different service type. Create an interface endpoint for com.amazonaws.<region>.s3, pick your subnets (one ENI gets provisioned per AZ you select), and attach a security group allowing inbound HTTPS on port 443 from whatever source needs access: your VPC's CIDR, an on-premises range, or a peered VPC's address space.

Then comes the decision that actually matters: private DNS. Turning it on overrides the standard S3 DNS name so it resolves straight to the endpoint's private IPs, which is convenient right up until you're also running a gateway endpoint in the same VPC, at which point the two can conflict in ways worth testing before you rely on them in production.

Each interface endpoint generates two kinds of DNS names. A regional one, vpce-<id>-<hash>.s3.<region>.vpce.amazonaws.com, and a zonal one that bakes the AZ into the hostname. Use the zonal version only when you're deliberately isolating traffic by AZ to cut down on same-region data transfer costs. It has no role to play in a cross-region setup.

For on-premises access, your on-premises DNS resolver needs a way to reach into the VPC's resolver, typically through a Route 53 Resolver inbound endpoint, or you update the on-premises DNS server directly to point S3 hostnames at the ENI private IPs.

Cross-region access, before November 2025, required a workaround worth understanding even now, because plenty of existing architectures still run on it. Picture an EC2 instance in us-west-2 that needs private access to a bucket sitting in us-east-1. Gateway endpoints won't get you there, and interface endpoints don't cross regions by default either. So the fix was: create an interface endpoint with private DNS turned off in the us-east-1 VPC, peer that VPC to the us-west-2 VPC, then build a Route 53 Private Hosted Zone for s3.us-east-1.amazonaws.com and associate it with the us-west-2 VPC. Add alias A records, root blank and a wildcard, pointing at the endpoint's regional DNS name.

One step gets missed constantly: the EC2 instance in us-west-2 also needs its own regional S3 endpoint, s3.us-west-2.amazonaws.com, to resolve which region a bucket lives in when a request doesn't specify --region explicitly. The fix is cheap, a free gateway endpoint in that same VPC, but skip it and you'll spend an afternoon chasing a resolution failure that looks like it should be a peering problem and isn't. Route tables in both VPCs need updating to send the peered CIDR range through the peering connection, and the two VPCs need non-overlapping CIDR blocks from the start, a prerequisite that's easy to overlook until it isn't.

A few other interface endpoint quirks worth knowing. FIPS-compliant workloads use the service name com.amazonaws.<region>.s3-fips, which generates its own set of DNS names and supports IPv4, IPv6, and dual-stack. And interface endpoints don't support everything S3 supports: no website endpoints, no legacy global endpoints, no S3 dash-region endpoint format, no CopyObject or UploadPartCopy between buckets in different AWS Regions, and no TLS 1.3 (though to be fair, TLS 1.0 and 1.1 are unsupported across AWS endpoints generally, that's not an interface-endpoint-specific gap).

On November 19, 2025, AWS extended native cross-region PrivateLink connectivity to Amazon-managed services, S3 included. Before that date, an interface endpoint only reached services in its own region, full stop, which is exactly why the VPC peering and Private Hosted Zone workaround above existed in the first place.

The rollout got there in stages. Cross-region PrivateLink for non-managed services launched in November 2024 across US East (N. Virginia), US West (Oregon), EU (Ireland), and several Asia Pacific and South America regions. December 2024 added 14 more regions, including Ohio, Canada Central, Stockholm, Paris, London, and Frankfurt. March 2025 folded in Canada West (Calgary), EU (Zurich), Middle East (UAE), and a few more Asia Pacific regions. Then, in November 2025, Amazon-managed services, S3 among them, got the same treatment.

Using it isn't automatic. It requires the vpce:AllowMultiRegion permission, which same-region interface endpoints never needed, and the target region has to be opted in before it'll work. There's also a new condition key, aws:SourceVpcArn, available for tightening access control. Pricing sits on top of the usual interface endpoint rates ($0.01 per hour per AZ, $0.01 per GB) plus standard EC2 inter-region data transfer charges, so cross-region PrivateLink is not free traffic, just simpler traffic. And only interface endpoints get this capability. Gateway endpoints and load balancer endpoints are left out entirely.

What this actually buys you: the VPC peering plus Private Hosted Zone dance is no longer required for most cross-region S3 access going forward. That older pattern still works, and it's still the right call for architectures that already have peering wired up or want to dodge inter-region PrivateLink charges altogether. It's not obsolete. It's optional now instead of mandatory.

The two-policy model: endpoint policy and bucket policy working together

An endpoint policy is a filter sitting on top of everything else, not a replacement for IAM or bucket policies. A request has to clear the endpoint policy and the IAM or bucket policy to actually go through. Miss either gate and the request fails.

Left on its default setting, an endpoint policy allows every S3 action for any principal whose IAM permissions already permit it, which sounds harmless until you realize it means anyone in the VPC with S3 access via IAM can use that endpoint to reach any bucket, not just the ones you had in mind when you built the thing. A custom policy narrows that down: specific actions, specific bucket ARNs, specific principals. Lock it to a single production bucket and it doesn't matter what else IAM grants access to elsewhere. The endpoint simply won't carry traffic to anything outside that list.

Here's the part that catches people off guard: an endpoint policy only governs traffic that passes through the endpoint. If the bucket itself still allows public access, someone can walk right past your endpoint restriction over the open internet and it'll never know the difference. Closing that gap needs a matching condition on the bucket policy side, specifically aws:sourceVpce, which restricts bucket access to traffic arriving through a named VPC endpoint ID. Set that condition and the endpoint restriction actually means something instead of being security theater.

One more substitution worth remembering: aws:SourceIp stops working once a request is traveling through a VPC endpoint. It just won't evaluate the way you expect. The correct condition key for that scenario is aws:VpcSourceIp, and mixing the two up is a quiet, easy way to end up with a bucket policy that looks locked down on paper and isn't locked down at all in practice.

Sources

  1. AWS PrivateLink for Amazon S3 - Amazon Simple Storage Service
  2. Set up private access to an Amazon S3 bucket through a VPC endpoint - AWS Prescriptive Guidance
  3. Gateway endpoints for Amazon S3 - Amazon Virtual Private Cloud
  4. Use VPC endpoints to access Amazon S3 buckets from EC2 instances
  5. Limit Amazon S3 bucket access to certain IPs or VPCs
  6. usage.ai
  7. repost.aws
  8. doit.com

More in Multi-Cloud Storage