AWS VPC Endpoints

A VPC Endpoint lets resources in a VPC reach AWS services without traversing the internet — no IGW, no NAT, no public IPs. There are two flavours with very different plumbing: Gateway Endpoints (for S3 and DynamoDB only) and Interface Endpoints (for almost everything else, powered by PrivateLink). Getting this right saves money, improves security, and removes failure modes.

Why endpoints exist

Default AWS service calls from a VPC go out via the NAT Gateway / Internet Gateway, hit the service’s public endpoint, come back. That means:

  • NAT Gateway data-processing charges — often the single biggest VPC cost line
  • Traffic traverses the internet (even if it ends up on AWS-owned IPs) — wasted hops, exposure
  • No private-path guarantee — auditors complain, compliance questions multiply

A VPC Endpoint solves all three: the call stays on the AWS network, reaches the service privately, no NAT.

The two flavours

Gateway EndpointInterface Endpoint
Services supportedOnly S3 and DynamoDBHundreds — most AWS services, plus third-party PrivateLink services
How it worksAdds a route to the VPC route table pointing at a vpce-* targetCreates ENIs in your subnets with private IPs
AddressingNo IP — route-table target onlyPrivate IP per AZ, resolvable via Route 53
DNS overrideOptional (for S3)Optional “private DNS” rewrites service hostname to the ENI IP
CostFree + free data transfer$0.01/hr/AZ + per-GB data processing
Cross-VPC via PrivateLinkNoYes
Cross-regionNoNo (same region)
Access controlVPC Endpoint Policy (IAM-style resource policy)Endpoint policy + security group on the ENI

TL;DR: use a Gateway Endpoint for S3 and DynamoDB (free — there’s no reason not to). Use Interface Endpoints for everything else where you want private path.

Gateway Endpoints — the S3 / DynamoDB special case

A Gateway Endpoint isn’t actually an endpoint in the ENI sense — it’s a route-table target. You create the endpoint and AWS adds a prefix-list route to your chosen route tables:

Route table (private subnet):
  10.0.0.0/16      → local
  0.0.0.0/0        → nat-xxxx
  pl-63a5400a (S3) → vpce-xxxx     ← Gateway Endpoint

The pl-63a5400a prefix list is AWS-managed — it contains all S3 IP ranges for the region. Longest-prefix-match sends S3-bound traffic to the endpoint instead of NAT.

Why this matters for bill:

  • Without gateway endpoint, S3 traffic from private subnets goes via NAT Gateway → per-GB data-processing charge
  • With gateway endpoint, S3 traffic bypasses NAT entirely — free

On a busy S3 workload, this single endpoint saves thousands/month. It should be the default on any private-subnet VPC that talks to S3.

An Interface Endpoint creates an ENI with a private IP in each subnet you specify. The service is accessed via that IP.

Old behaviour:  client → DNS: secretsmanager.us-east-1.amazonaws.com → 52.x.x.x (public)
                      → IGW/NAT → internet → Secrets Manager

New behaviour:  client → DNS: secretsmanager.us-east-1.amazonaws.com → 10.0.1.15 (private, ENI)
                      → directly to the service over PrivateLink

The magic: Private DNS (enabled by default on most service endpoints) overrides the default resolution inside the VPC so the service’s normal hostname resolves to the private ENI IP. Apps don’t need code changes.

PrivateLink is the mechanism. It provides a point-to-point connection into the service’s VPC without any routing exposure between VPCs. The same technology powers:

  • Interface endpoints to AWS services
  • VPC Endpoint Services — you publish your own service (behind an NLB) for other VPCs/accounts to consume via PrivateLink

Third-party SaaS vendors (Snowflake, Datadog, MongoDB Atlas, etc.) expose their services as PrivateLink endpoints — you consume them as interface endpoints without internet transit.

Security group on the endpoint ENI

Every Interface Endpoint has an ENI, and every ENI has an SG. By default it allows all inbound from the VPC — usually too permissive. Lock it down to specific SGs or CIDRs for tighter blast-radius control.

DNS behaviour

Gateway Endpoint (S3): no DNS change needed. Apps keep using s3.amazonaws.com; the route table handles forwarding.

Interface Endpoint: three relevant DNS records created:

  1. Regional namevpce-xxxx.<service>.us-east-1.vpce.amazonaws.com
  2. Zonal names — per-AZ variants
  3. Private-DNS override — the default service hostname resolves to the ENI IP from inside the VPC (if Private DNS is enabled)

Private DNS caveat: requires enableDnsSupport and enableDnsHostnames both true on the VPC. If disabled, apps must use the regional endpoint name explicitly.

VPC Endpoint policies

Both endpoint types can have a policy that restricts what actions/resources the endpoint can be used to call.

Example — S3 Gateway Endpoint policy allowing only one bucket:

{
  "Statement": [{
    "Effect": "Allow",
    "Principal": "*",
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:::my-approved-bucket/*"
  }]
}

Combined with a bucket policy that requires the aws:sourceVpce condition, you build a bucket that can only be reached from specific VPCs — powerful for data exfiltration controls.

Common use cases

Use caseEndpoint
Private subnet needs S3 without NATS3 Gateway Endpoint
EKS cluster pulling ECR imagesECR API + ECR DKR Interface Endpoints
Secrets Manager / Parameter Store access from private workloadsInterface Endpoint
CloudWatch Logs from a no-internet VPCCloudWatch Logs Interface Endpoint
SSM Session Manager to private instancesssm, ssmmessages, ec2messages Interface Endpoints (all three needed)
Lock down an S3 bucket to specific VPCsGateway Endpoint + bucket policy aws:sourceVpce
Expose a service in VPC-A to apps in VPC-BVPC Endpoint Service (PrivateLink)
Consume a SaaS product without internetInterface Endpoint to the vendor’s PrivateLink service

Cost considerations

  • Gateway endpoints: free to create, free to use — always add one for S3/DynamoDB on private subnets.
  • Interface endpoints: 7/month for a 3-AZ setup) + $0.01/GB processed. For low-volume services this is often close to break-even against NAT-gateway costs. At any reasonable volume, interface endpoints are cheaper.
  • SSM endpoints trio — to run private instances without NAT, you need ssm + ssmmessages + ec2messages — three endpoints = three × per-AZ charges. Still usually worth it for security.

Centralised endpoints pattern

For a Transit Gateway-centric network, instead of putting endpoints in every VPC, you can:

  1. Place interface endpoints in a shared services VPC
  2. Route DNS / traffic through TGW to that VPC
  3. Set up Route 53 Private Hosted Zones shared across VPCs for name resolution

Pros: fewer endpoints to pay for, centralised policy. Cons: more moving parts, TGW data charges per byte. Reasonable for large estates (10+ VPCs); overkill for 2-3.

Common pitfalls

  1. No S3 Gateway Endpoint on a heavy-S3 private subnet. NAT bill balloons. Free fix.
  2. Interface endpoint without Private DNS enabled — apps still resolve to the public endpoint and hit NAT. Check the “Enable Private DNS” checkbox.
  3. Overlapping service endpoints in multiple VPCs — wasteful if you could centralise.
  4. Forgetting the SSM trio. Session Manager to private instances needs all three endpoints or the agent fails.
  5. SG on endpoint too permissive. Default allows all inbound from the VPC. Tighten.
  6. Endpoint in one AZ, instances in another. Interface endpoints are AZ-scoped ENIs — place them in all AZs your workloads live in, or you’ll cross-AZ charge.
  7. Endpoint policy conflict. Endpoint policy, IAM policy, bucket policy, KMS key policy — all must allow. Too-strict endpoint policy silently denies everything.
  8. Expecting cross-region. Endpoints are regional; you can’t reach us-east-1 S3 via a eu-west-1 endpoint.

Mental model

  • Gateway Endpoint = a route-table entry that diverts S3/DynamoDB traffic off the internet. No IPs, no SG, no cost.
  • Interface Endpoint = an ENI in your VPC backed by PrivateLink, plus a DNS override. Costs per hour + per GB.
  • PrivateLink = the underlying mechanism for interface endpoints and cross-VPC/cross-account service exposure.
  • Endpoint policy + bucket/KMS policy together = how you build “S3 reachable only from my VPCs” guardrails.

See also