Skip to content
Networking4 min read

An interface endpoint with private DNS off is a bill with no benefit

It costs the same per hour as one that works, reaches available, passes every reachability check, and nothing routes to it. The Terraform default is false, which is most of why this ships.

· Venerable Networks

An interface VPC endpoint does two separable things. It puts an ENI in your subnet that fronts an AWS service, and — optionally — it claims that service's regional hostname inside your VPC so unmodified code uses it.

Only the first one is on by default in Terraform. private_dns_enabled defaults to false.

An endpoint in that state is created successfully, reaches available, has a healthy ENI, passes every reachability test you point at it, and is used by nothing. It also bills at exactly the same rate as one that works.

What the failure looks like

A workload in a private subnet with no internet route calls Secrets Manager at startup. The endpoint exists. The security group permits 443 from the VPC. The instance role has GetSecretValue on the secret.

Every call hangs and times out. No permission error, no TLS error.

Resolve the hostname and the reason is immediate:

getent hosts secretsmanager.us-east-1.amazonaws.com
# 3.234.16.98     secretsmanager.us-east-1.amazonaws.com
# 44.208.11.7     secretsmanager.us-east-1.amazonaws.com

Public addresses. Nothing inside the VPC answers for that name, so Route 53 returns the service's public regional records — which is the correct answer to the question asked. There is simply no private override.

The workload then opens a TCP connection to a public address from a subnet whose route table contains only the local route. The packet matches no route, is discarded, and nothing is denied so nothing is logged. The client sits until its connect timeout expires.

Compare against an endpoint that has private DNS on and the contrast is one line:

getent hosts ssm.us-east-1.amazonaws.com
# 10.80.1.171     ssm.us-east-1.amazonaws.com

The symptom points away from DNS

The client reports a connect timeout, not a resolution failure. That is why DNS gets eliminated early.

Both hostnames resolved successfully. One resolved to somewhere unreachable. A name that resolves to an unreachable address produces exactly the same symptom as a routing or firewall problem — because by the time the packet is sent it is a routing problem, created two layers up.

Worth adding as a special case to whatever triage order you use: a timeout to an AWS service hostname from a private subnet should send you to getent hosts before the security group. A public answer explains the entire failure in one line.

Proving the endpoint is fine

An endpoint always has its own DNS names whether or not it claims the service hostname. Using one directly separates "the endpoint is broken" from "nothing is routing to the endpoint":

aws secretsmanager get-secret-value --secret-id my-secret \
  --endpoint-url https://vpce-0a1b2c3d-4e5f6g7h.secretsmanager.us-east-1.vpce.amazonaws.com

If that works, the endpoint, its security group, its subnet placement, and your IAM policy are all correct, and the only thing wrong is which address the service name produces. It is the fastest way to cut the search space in half.

The prerequisite that fails identically

Private DNS requires both enableDnsSupport and enableDnsHostnames on the VPC. Enable private_dns_enabled in a VPC where those are off and it will not have the effect you expect, and the symptom is the same connect timeout.

So there are two independent causes producing one indistinguishable failure, checked at different layers. Check the VPC attributes and the endpoint attribute, in that order.

Finding them in an account

Most endpoints with private DNS disabled are mistakes. Some are deliberate. Either way they are worth listing:

aws ec2 describe-vpc-endpoints \
  --filters Name=vpc-endpoint-type,Values=Interface \
  --query 'VpcEndpoints[?PrivateDnsEnabled==`false`].[VpcEndpointId,ServiceName,VpcId]' \
  --output table

The legitimate reasons are narrow. The main one is a VPC where some workloads must reach a service's public API while others use the private path — claiming the regional hostname is all-or-nothing per VPC, so you leave private DNS off and point the private clients at the vpce-… name explicitly. The other is a transition window while clients migrate. If neither applies, the flag should be on.

Two adjacent things that are easy to conflate

Cross-VPC and on-premises access needs more than the flag. Private DNS creates an AWS-managed private hosted zone associated with the endpoint's VPC only. A peered VPC or an on-premises resolver asking for that hostname gets the public answer, exactly as the broken workload did. Reaching an endpoint from elsewhere means a Route 53 Resolver inbound endpoint with forwarding rules, or your own private hosted zone with an alias to the endpoint.

Endpoint policies fail loudly instead. An interface endpoint carries a policy that defaults to full access. A restrictive one produces AccessDeniedException naming the endpoint — traffic arrived and was refused. Timeout means it never arrived. The two are easy to tell apart once you know to look.

The check that would have caught it

Not a reachability test against the ENI; that passes. Assert that the service hostname resolves inside the VPC:

getent hosts secretsmanager.us-east-1.amazonaws.com | grep -q '^10\.' \
  || echo "FAIL: service hostname is not resolving privately"

One line in a smoke test after any endpoint change.

There is a lab that deploys this state, with three working Systems Manager endpoints sharing the same security group so the security-group explanation dies in one command: the endpoint that resolved to the internet. The NAT Gateway guide covers when an interface endpoint is worth its hourly cost at all, which is a question worth asking before this one.