all writing connectivity

What "private" actually means. PrivateLink, Private Link, and PSC under the hood

Three clouds, three names that all mean roughly the same thing, and three implementations that are close enough to rhyme and different enough to bite you. This is the deep-dive I wished existed the last time I had to explain to a customer why their PrivateLink bill didn’t match their mental model — so here’s the mechanics, the genuine differences, and the gotchas, cloud by cloud.

The shared problem all three are solving: expose a service across a trust boundary — VPC to VPC, account to account, tenant to tenant — without peering, without touching the public internet, and without the N² routing mess that full-mesh peering creates at scale. All three clouds landed on a strikingly similar answer. None of them tunnel. None of them peer. All three terminate the connection at an interface object that lives inside your subnet, and let an internal SDN fabric do the real work of stitching it to the other side.

When you provision an Interface VPC Endpoint, AWS doesn’t give you a route to a remote CIDR. It gives you an ENI — one per subnet/AZ you select — with a private IP carved out of your own address space. Your instance resolves the service’s DNS name, gets that ENI’s IP back, and connects to it directly. As far as your route table is concerned, nothing remote is involved at all.

On the provider side, the pattern is a Network Load Balancer (or, for inline inspection use cases, a Gateway Load Balancer) fronting the actual service, exposed as a VPC Endpoint Service.

The traffic doesn’t actually flow through your ENI as a conventional hop. It’s intercepted at the Nitro/hypervisor layer by Hyperplane — the same internal distributed system that underpins NLB and NAT Gateway — which performs the SNAT/DNAT translation and forwards the packet across AWS’s internal backbone to the provider’s NLB. Your ENI is the address the connection is addressed to; Hyperplane is what actually moves the bytes.

This gives PrivateLink a star topology instead of a mesh: thousands of consumer VPCs can connect to one provider service with zero routing entries and zero CIDR-overlap risk, because there’s no peering relationship underneath to create one.

Gotchas worth knowing:

Azure’s consumer-side primitive is the Private Endpoint — a read-only network interface Azure places in your subnet, with a dynamic private IP retained for the endpoint’s lifetime. It’s scoped tightly: one NIC maps to exactly one PaaS resource instance — one specific storage account, one SQL logical server, one Key Vault — not the service category as a whole. Connections are strictly unidirectional and TCP-only: the client initiates, the service can never push traffic back into your VNet.

On the provider side, you put your own service behind a Standard Azure Load Balancer to make it Private Link-capable, and consumers connect to it through a Private Endpoint in their own VNet, gated by an approval call flow you control.

The underlying SDN fabric doing the actual NAT/translation work isn’t documented with a public name the way Hyperplane is — but architecturally it has to be doing the same job: terminate at the NIC, translate, forward over Microsoft’s backbone.

Where Azure genuinely diverges, and where it bites:

GCP Private Service Connect: two consumer modes and a third primitive nobody else has

GCP’s model splits into more pieces than the other two. On the consumer side you choose between an Endpoint — a forwarding rule with an internal IP, the direct equivalent of AWS/Azure’s model — or a Backend, where a PSC Network Endpoint Group sits behind your own GCP load balancer instead of being addressed directly. The Backend option buys you things the other clouds don’t expose as cleanly: your own domain and certificate in front of someone else’s managed service, consumer-controlled failover across regions, and centralized access control at the LB layer.

On the producer side, you publish a Service Attachment pointing at an internal load balancer, and consumers connect to it via forwarding rules.

The fabric underneath is Andromeda — Google’s SDN substrate, playing the same architectural role as Hyperplane. The packet path is genuinely instructive: traffic moves directly from the physical host running the client VM to the physical host running the producer’s load balancer VM. The source host makes the client-side load-balancing decision, wraps the packet in an Andromeda header identifying the destination VPC, and the destination host performs SNAT/DNAT on arrival. Same shape as Hyperplane, different name, same job.

The genuine architectural outlier: PSC interfaces. Unlike AWS and Azure, where the model is strictly client-initiates-only, GCP has a third primitive — PSC interfaces — that allows the producer to initiate connections back to the consumer, bidirectionally. This isn’t a minor naming difference; it breaks an assumption that holds everywhere else in this comparison.

Gotchas specific to PSC:

The one thing to take away

Strip the branding and all three converge on the same primitive: an interface object lives in your subnet, a hyperscale internal fabric (Hyperplane / Andromeda / Azure’s unnamed equivalent) does hypervisor-level NAT to stitch it to the other side, and none of it touches a route table or the public internet. That’s why CIDR overlap, the thing that makes VPC peering and transit gateways painful at scale, simply isn’t a constraint here — there’s no route exchange to collide.

Where they actually differ is at the edges. GCP trades simplicity for optionality: an extra consumer-side mode, a genuinely bidirectional primitive, and a producer-side NAT subnet you now have to capacity-plan. Azure trades that optionality for the sharpest DNS-dependency story of the three, plus a real observability gap that’s only now closing. AWS sits in the middle — the most “just works” of the three once your security groups and endpoint policies are both correctly wired, which is itself the most common way to get it wrong.

If you’re designing for production, the practical takeaway isn’t “which one is better” — it’s that the failure modes are cloud-specific even when the architecture isn’t. Test your DNS resolution path on Azure before you test anything else. Capacity-plan your NAT subnet on GCP before you onboard your tenth consumer. And on AWS, draw out your SG-and-endpoint-policy intersection before you assume a denied request is a routing problem.

Written by Paul Carvill

Enterprise → cloud → AI networking. I write the breakdowns I wish I’d had. New field notes roughly twice a month.

keep reading

More writing