Reaching AWS Services from Private Subnets with Interface Endpoints: An Architecture Note
Interface VPC endpoints let private subnets reach SQS, Secrets Manager, and KMS without a NAT gateway. Here's the minimal design, trust boundaries, checks, and failure modes.
02 May 2026, 21:04 UTC

The problem and the takeaway
Workloads in private subnets that call SQS, Secrets Manager, or KMS normally need a NAT gateway, which adds hourly cost, data-processing charges, and a public egress path you may not want. VPC interface endpoints (AWS PrivateLink) put a private network interface for the service inside your VPC, so SDK calls stay on the AWS network with no code changes. The smallest suitable design is one interface endpoint per required service, spread across two or more Availability Zones, with a restrictive endpoint policy and a security group allowing 443 from your workload subnets.
Requirements
- Your workloads run in private subnets and call AWS APIs (SQS, Secrets Manager, KMS, etc.).
- You want to remove or avoid NAT gateway cost and public egress for those calls.
- The target service supports PrivateLink in your region — confirm this first, because not every service does.
The smallest suitable design
An interface endpoint creates ENIs with private IPs in your subnets. When private DNS is enabled, the standard service hostname (for example sqs.us-east-1.amazonaws.com) resolves to those private IPs from inside the VPC, so existing SDK code keeps working unchanged.
Minimal Terraform sketch (run wherever you manage infrastructure, with permissions for ec2:CreateVpcEndpoint and related calls):
resource "aws_vpc_endpoint" "secretsmanager" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.us-east-1.secretsmanager"
vpc_endpoint_type = "Interface"
subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
security_group_ids = [aws_security_group.endpoints.id]
private_dns_enabled = true
policy = jsonencode({
Statement = [{
Effect = "Allow"
Principal = "*"
Action = ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"]
Resource = "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/*"
}]
})
}The endpoint security group needs an inbound rule for TCP 443 from your workload subnets' CIDRs (or their security groups). Place endpoint ENIs in at least two AZs so an impaired ENI in one AZ doesn't break resolution.
Note the pricing model: gateway endpoints exist only for S3 and DynamoDB, are free, and use route tables. Everything else uses interface endpoints, billed hourly per AZ plus per-GB processed. At very low call volume, a NAT path can be cheaper; at sustained volume, endpoints usually win.
Trust and data boundaries
Traffic to the service stays on the AWS network, so you can remove the NAT route for those calls and shrink the public attack surface. The endpoint policy is a second control layer alongside IAM: a deny at the endpoint blocks a call even if the IAM role allows it. That makes endpoints useful for enforcing a per-VPC data perimeter — for example, restricting which Secrets Manager ARNs are reachable from this VPC at all.
Operational checks
From an EC2 instance or Lambda in the workload subnet:
nslookup secretsmanager.us-east-1.amazonaws.comExpected: it resolves to private IPs inside your VPC CIDR. If it resolves to public IPs, private DNS is off or not applying — calls will silently route via the public path or fail if there's no NAT.
Then prove the path: temporarily remove the NAT route in a test subnet and confirm SDK calls to the service still succeed while public internet calls fail. Finally, add a temporary deny to the endpoint policy and confirm the call returns an access-denied error — that proves the policy layer is actually active.
Failure modes
- Private DNS disabled or overridden: calls fall back to public resolution and fail in a no-NAT subnet. Check resolution first.
- Security group drift: someone tightens the endpoint SG and 443 from workloads is dropped; calls time out rather than fail fast.
- Endpoint policy drift: a policy change blocks legitimate actions; errors surface as access-denied at the application, which looks like an IAM problem.
- Single-AZ placement: an impaired endpoint ENI in one AZ breaks that AZ's workloads.
When the design changes
Reconsider if you need cross-region or cross-account service access (centralized endpoint designs require custom DNS via Route 53 Resolver rules or private hosted zone sharing), if call volume is so low that endpoint hourly charges exceed NAT costs, or if the service lacks PrivateLink support in your region. Verify service support and current pricing before committing — both change over time.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.