Skip to content
SpecialtyFreePrivateLinkNACL / Security GroupsAWS VPC

Lab 04: The Endpoint That Answered To Nobody

A PrivateLink interface endpoint reports available, DNS resolves to it, the load balancer behind it has a healthy target, and the application security group allows all egress. Every connection still times out, and nothing in the consumer configuration looks wrong.

Debugging time
~30 min
Reading time
12 min
Reported by
Integrations
Tier
Specialty
INC-1207SEV-3InvestigatingOpened Today 09:31 UTC

Application cannot reach the vendor API through its new PrivateLink endpoint

Reported by Integrations

Environment
staging
Region
us-east-1
Consumer VPC
10.20.0.0/16
Endpoint type
Interface (PrivateLink)
Change ref
CHG-2410 — replace public egress with PrivateLink

We moved the vendor API integration off public internet egress and onto PrivateLink, so the traffic never leaves AWS. The endpoint came up clean. The application cannot connect.

What we have confirmed:

  • The VPC endpoint state is available, not pending or pendingAcceptance.
  • The endpoint DNS name resolves from the application host, to an address inside our own subnet.
  • The application's security group allows all egress. We widened it to be certain.
  • There are no custom network ACLs on the subnet — it is the VPC default, allow all.
  • The route table is untouched. The endpoint address is in our own VPC CIDR, so it is a local route.
  • On the provider side the load balancer target is healthy.

Every layer we know how to check reports healthy, and curl hangs until it times out. We are starting to think PrivateLink is broken, which we accept is unlikely.

What you are working with

Two VPCs. The provider hosts a service behind an internal Network Load Balancer, exposed as a VPC endpoint service. The consumer reaches it through an interface endpoint. There is no peering, no Transit Gateway, and no internet path between them — PrivateLink is the only connection.

10.20.1.30:41902 → endpoint ENI:8080 (TCP SYN)
  1. EC2

    Application host

    10.20.1.30 in the consumer VPC

  2. FILTER

    Application security group — egress

    sg-app — allow all outbound

  3. RTB

    Consumer VPC route table

    10.20.0.0/16 → local — the endpoint ENI is in this subnet

  4. VPCE

    Interface endpoint ENI

    vpce-… — state: available

  5. GW

    Endpoint service → NLB

    healthy target in the provider VPC

  6. DEST

    Service backend

    10.10.1.20:8080 — listening

Everything the team checked is on this diagram and all of it passes. The hop they did not think to inspect is the endpoint itself.

Scope and constraints

  • In scope: the interface endpoint and what governs traffic reaching it.
  • Out of scope: the endpoint service, the NLB, its target group health, the backend service, DNS, routing, and network ACLs. All are correct — the ticket's checks were accurate.
  • The consumer's application security group is not the problem. It permits all egress and widening it further will not help. Read that sentence carefully; it is true and it is also a hint.
  • You get a shell on both hosts. The backend is reachable directly from within the provider VPC if you want to confirm the service works.

Deploy the broken state

main.tf8 lines

Download the file below into an empty directory and apply it. Expect five to seven minutes — the NLB and the endpoint service both take a while to become available.

terraform init
terraform apply

# Shell on the consumer application host
terraform output -raw consumer_session_command

# The URL the application uses
terraform output -raw endpoint_url

Full source: main.tf. It builds both VPCs, the internal NLB with a health-checked target group, the endpoint service with the consumer registered as an allowed principal, the interface endpoint, and one instance per side.

Two implementation notes so they do not read as bugs. private_dns_enabled is false because private DNS on a custom endpoint service requires domain ownership verification — consumers use the generated endpoint DNS name, which is the normal pattern for third-party services. And the target group sets preserve_client_ip = "false" so the backend sees a source address inside the provider VPC, keeping backend security group rules from becoming a second, unintended failure.