The pods from the last post are perfectly isolated. That’s the requirement, and straight away the problem. Every one of them needs Windows updates, a package repo, DNS, maybe a domain controller. Do I really run a WSUS (Windows Server Update Services) per pod?

No. There’s a third subnet access mode for exactly this. The design it enables is hub-and-spoke, with a very specific property: spokes reach the hub; the hub cannot reach the spokes; spokes never reach each other.

The three access modes

Everything in this series comes down to one field on a VPC subnet, which is either elegant or slightly deflating, depending on the day:

accessModeAdvertised toUse
Privatenobody outside the VPCworkloads — isolation and overlapping CIDRs
PrivateTGWevery VPC attached to the org’s transit gatewayshared services
Publicthe external networkinternet/corp-facing endpoints

PrivateTGW (private transit gateway) subnets draw their addresses from a separate transit block (here 172.31.0.0/…), not from the VPC’s own privateIPs. That’s the key. The shared range can’t collide with the pods' 172.30.0.0/16, because it comes from a different pool that all VPCs agree on.

The build

One more VPC, shared-svc, follows the same ordering rules as any other: VPC, then VPCAttachment, then LoadBalancer, then namespace. Then a single subnet:

apiVersion: crd.nsx.vmware.com/v1alpha1
kind: Subnet
metadata: {name: sn-services}
spec: {accessMode: PrivateTGW, ipv4SubnetSize: 32}

It realized as 172.31.0.0/27. A VM on it, svc-repo01 at 172.31.0.2, serving HTTP, became the shared repo. Not the most glamorous job in the lab, but somebody has to do it.

Three pod VPCs with identical 172.30.0.0/16 Private subnets reach the shared-svc VPC’s repo at 172.31.0.2 through the transit gateway, each SNATed to its own address; pods can’t reach each other, and the repo can’t route back to a pod One way by construction: the pods reach the hub, and nothing reaches a pod.

The test that matters is directional

Reachability to the service is the easy claim. From esx01 in each of the three pods (ESXi ships python3, so urllib is the test client):

pod-a esx01 -> http://172.31.0.2/   200  shared-svc repo01
pod-b esx01 -> http://172.31.0.2/   200  shared-svc repo01
pod-c esx01 -> http://172.31.0.2/   200  shared-svc repo01
default-vpc  -> http://172.31.0.2/   200  shared-svc repo01

Three pods with identical source addresses (172.30.0.40) all hit one service, and all get answers. So how does each reply find its way back to the right pod, when three of them claim .40?

Because pod traffic crosses the transit gateway SNAT’d to a per-VPC address from the external IP block, by the default outbound NAT in each VPC’s connectivity profile. The service never sees 172.30.0.40; it sees three distinct SNAT addresses. Ambiguity never arises.

Now the other direction, from svc-repo01 back towards a pod:

svc-repo01 -> 172.30.0.40   unreachable
svc-repo01 -> 172.30.0.41   unreachable

Not “blocked”: unroutable. Pod subnets are Private, so they were never advertised to the transit gateway. Even if they had been, 172.30.0.40 would be ambiguous across three VPCs. The hub literally cannot start a conversation with a spoke.

For a shared service that will one day be compromised, that’s the property you want. Gloomy, I know, but gloom is a perfectly good security posture.

What goes in the hub

Anything that pods consume and that is stateless about which pod is asking: WSUS and patch mirrors, OS and package repos, container registries, NTP, DNS forwarders, licence servers. Domain controllers work too, with the usual caveat: identical hostnames across pods need per-pod domains or a naming scheme.

What does not go in the hub: anything that needs to reach into a pod, such as monitoring pollers, backup agents that pull, and jump hosts. Those either live in the pod, or the pod publishes a LoadBalancer VIP for them: the deliberate door from part 0.

Tightening further

The transit gateway gives you reachability; policy gives you precision. A VPCGatewayFirewallPolicy on shared-svc can restrict inbound traffic to tcp/80,443 from the transit range and nothing else. Then the repo is a repo, and not a foothold.

I left it open for the test. You shouldn’t: do as I say, not as I did.

Why this matters outside the lab

This is the pattern that makes isolated tenants affordable. Without it, every isolated environment needs its own patch server, repository, DNS and directory. That’s cost and drift, and they quietly kill the idea.

With it, a customer runs one set of shared services for dozens of tenants, and keeps them patched in one place. They can still show a security reviewer that the shared service has no path back into any tenant.

The same hub serves well beyond patching: central logging and monitoring collectors, licence servers, artifact registries, build agents. Anything tenants consume, but shouldn’t be reachable by.

Rules learned

  • PrivateTGW is the shared-services mode: addresses from the transit block, advertised to every attached VPC, no collision with pod space.
  • Access is one-way by construction: pods to service works (SNAT’d per VPC), and service to pod has no route. Test both directions and write down both results.
  • Identical pod addressing and shared services coexist because of the SNAT. The hub sees per-VPC SNAT addresses, never the overlapping private ones.
  • The same ordering rules apply to the hub VPC: VPC, attachment, load balancer, namespace, subnets, wait for image sync, then workloads.
  • Add a gateway firewall policy on the hub. Reachability is not authorisation.

Broadcom documentation

Previously: three datacenters, one IP plan. Next: the whole pod as a single catalog item.


Lab environment; opinions my own. Output captured live, trimmed for length, never edited for outcome.