<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Training-Labs on The Nested Lab</title>
    <link>https://thenestedlab.com/tags/training-labs/</link>
    <description>Recent content in Training-Labs on The Nested Lab</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <lastBuildDate>Wed, 16 Sep 2026 08:00:00 +0100</lastBuildDate>
    <atom:link href="https://thenestedlab.com/tags/training-labs/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Three datacenters, one IP plan: identical isolated pods with NSX VPCs</title>
      <link>https://thenestedlab.com/posts/three-datacenters-one-ip-plan/</link>
      <pubDate>Wed, 16 Sep 2026 08:00:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/three-datacenters-one-ip-plan/</guid>
      <description>Three nested-ESXi pods, byte-identical addressing — same subnets, same VLANs, same host IPs, even the same MACs — with zero reachability between them. How overlapping VPC CIDRs and deterministic subnet realization turn cookie-cutter environments into a first-class feature.</description>
      <content:encoded><![CDATA[<p>Here are three hosts, all answering to <code>vmk0 = 172.30.0.40</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ssh root@192.168.144.30  -&gt;  [root@esx01-a:~]  vmk0  172.30.0.40
</span></span><span class="line"><span class="cl">ssh root@192.168.144.32  -&gt;  [root@esx01-b:~]  vmk0  172.30.0.40
</span></span><span class="line"><span class="cl">ssh root@192.168.144.34  -&gt;  [root@esx01-c:~]  vmk0  172.30.0.40
</span></span></code></pre></div><p>Same IP. Same VLAN. Same gateway. Same <em>MAC address</em>, as it turns out. And
none of them can reach any of the others. This is the post where NSX VPCs
stop being a workaround for nested labs and become genuinely better than
the physical alternative.</p>
<p><img alt="Three pods, identical IP plans, no route between them" loading="lazy" src="/images/product-01-hook.jpg"></p>
<h2 id="why-identical-addressing-matters">Why identical addressing matters</h2>
<p>If you&rsquo;ve ever built training pods, cert-study labs, or per-team
reproduction environments, you know the pain: every copy needs a unique
address plan, so every runbook, every screenshot, every &ldquo;type this exact
command&rdquo; has to be parameterised per pod. Students in seat 7 see different
numbers from the slides. Reproductions drift from the original.</p>
<p>The fix is obvious and normally impossible: <strong>give every pod the same
addresses</strong>. On a physical fabric that means VRFs, per-pod NAT, and a
network team that stops answering your emails. In an NSX VPC it&rsquo;s the
default behaviour.</p>
<h2 id="the-mechanism-overlapping-privateips">The mechanism: overlapping privateIPs</h2>
<p>Each pod gets its own VPC, and every VPC declares the same private range:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPC</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nested-vpc-a}     </span><span class="w"> </span><span class="c"># then -b, then -c</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">privateIPs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;172.30.0.0/16&#34;</span><span class="p">]</span><span class="w">      </span><span class="c"># identical in all three</span><span class="w">
</span></span></span></code></pre></div><p>A <code>Private</code> subnet is never advertised beyond its VPC, so NSX has no
objection to three VPCs carving up the same /16. The pods aren&rsquo;t
&ldquo;firewalled from each other&rdquo; — there is simply no route between them.
Isolation by construction, not by policy.</p>
<p><img alt="NSX: four VPCs, four sn-mgmt subnets, same CIDR" loading="lazy" src="/images/ui/u11a-nsx-snmgmt-four-vpcs.jpg">
<em>NSX&rsquo;s subnet view filtered to <code>sn-mgmt</code>: four rows, four VPCs, one CIDR.</em></p>
<h2 id="the-trick-deterministic-realization">The trick: deterministic realization</h2>
<p>Identical <em>ranges</em> aren&rsquo;t enough — I want identical <em>subnets</em>, so the
management gateway is <code>.33</code> and the hosts are <code>.40</code>/<code>.41</code> in every pod.
NSX allocates subnets from <code>privateIPs</code> in creation order, and a fresh VPC
allocates deterministically. So the topology is applied in a <strong>fixed
order</strong> — trunk, mgmt, vMotion, vSAN — and every pod realizes the same
map:</p>
<table>
	<thead>
			<tr>
					<th>Subnet</th>
					<th>Realized</th>
					<th>VLAN</th>
					<th>Hosts</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>sn-trunk</td>
					<td>172.30.0.0/27</td>
					<td>—</td>
					<td>(carries the tags)</td>
			</tr>
			<tr>
					<td>sn-mgmt</td>
					<td>172.30.0.32/27</td>
					<td>1610</td>
					<td>.40 / .41, gw .33</td>
			</tr>
			<tr>
					<td>sn-vmotion</td>
					<td>172.30.0.64/27</td>
					<td>1611</td>
					<td>.70 / .71, gw .65</td>
			</tr>
			<tr>
					<td>sn-vsan</td>
					<td>172.30.0.96/27</td>
					<td>1612</td>
					<td>.100 / .101, gw .97</td>
			</tr>
	</tbody>
</table>
<p>In the catalog blueprint that order is enforced with <code>dependsOn</code> between
the subnet resources — the one place a declarative tool needs to be told
about sequence. Skip it and two pods can come out with mgmt and vMotion
swapped, which works perfectly and confuses everyone.</p>
<p><img alt="NSX: nested-vpc-a expanded, the /16 private block" loading="lazy" src="/images/ui/u11-nsx-vpc-a-cidr.jpg"></p>
<h2 id="the-door-one-vip-per-host">The door: one VIP per host</h2>
<p>Each pod is unreachable from outside by design, so each host gets a
<code>VirtualMachineService</code> of type <code>LoadBalancer</code> publishing SSH and HTTPS.
The VIPs come from the org&rsquo;s <em>external</em> block, and they&rsquo;re the only
addresses that differ between pods:</p>
<table>
	<thead>
			<tr>
					<th>Pod</th>
					<th>VPC</th>
					<th>esx01 VIP</th>
					<th>esx02 VIP</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>a</td>
					<td>nested-vpc-a</td>
					<td>192.168.144.30</td>
					<td>.31</td>
			</tr>
			<tr>
					<td>b</td>
					<td>nested-vpc-b</td>
					<td>192.168.144.32</td>
					<td>.33</td>
			</tr>
			<tr>
					<td>c</td>
					<td>nested-vpc-c</td>
					<td>192.168.144.34</td>
					<td>.35</td>
			</tr>
	</tbody>
</table>
<p>Which is how the opening transcript works: three VIPs, three hosts, one
inside address.</p>
<h2 id="proving-the-isolation">Proving the isolation</h2>
<p>Claims are cheap. The test matrix, from a VM in a <em>fourth</em> VPC (the org
default):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ping 172.30.0.140 (own VPC)....... REACHABLE
</span></span><span class="line"><span class="cl">ping 172.30.0.40  (pod space)..... unreachable
</span></span><span class="line"><span class="cl">curl http://172.31.0.2/ (shared).. shared-svc repo01
</span></span></code></pre></div><p><img alt="Isolation matrix: own-VPC reachable, pod space unreachable, shared service reachable" loading="lazy" src="/images/demo-c7-isolation.jpg"></p>
<p>Its own VPC&rsquo;s <code>172.30.0.140</code>: reachable. <code>172.30.0.40</code> — an address that
exists in three other VPCs simultaneously: unreachable, because from here
there is no such route. (The third line is the shared-services VPC, which
is <a href="/series/the-vpc-pod-papers/">the next post</a>.)</p>
<p>Inside each pod, east-west is normal: <code>esx01 → esx02</code> vmkping passes on
all three VLANs, in all three pods. And the detail I didn&rsquo;t expect: the
nested-ESXi appliance derives vmk0&rsquo;s MAC deterministically from its
config, so <strong>the three hosts share a MAC as well as an IP</strong>. Harmless — each
VPC is its own L2 domain — but a nice demonstration of how complete the
separation is.</p>
<h2 id="what-this-replaces">What this replaces</h2>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Physical / VLAN-based pods</th>
					<th>VPC pods</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Identical addressing</td>
					<td>VRF per pod + NAT, fabric change per pod</td>
					<td>default behaviour</td>
			</tr>
			<tr>
					<td>Adding a pod</td>
					<td>switch config, IPAM, firewall rules</td>
					<td>one API call for the VPC, one blueprint request</td>
			</tr>
			<tr>
					<td>Isolation guarantee</td>
					<td>policy (auditable, breakable)</td>
					<td>topology (no route exists)</td>
			</tr>
			<tr>
					<td>Tenant self-service</td>
					<td>no</td>
					<td>yes — the VPC is a tenant object</td>
			</tr>
	</tbody>
</table>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>&ldquo;Identical environments&rdquo; sounds like a lab nicety. It&rsquo;s actually one of the
most requested things in enterprise IT, usually asked for in other words:</p>
<ul>
<li><strong>Training at scale</strong> — every seat in the room sees the same addresses as
the slides, so material is written once and never parameterised per pod.</li>
<li><strong>Per-engineer or per-team replicas</strong> of a reference environment, for
development and testing that behaves exactly like the original.</li>
<li><strong>Regulatory or business-unit separation</strong> on shared infrastructure
without VRF sprawl or a bespoke firewall estate — isolation is a property
of the topology, which is the easiest kind to evidence to an auditor.</li>
<li><strong>Blue/green copies</strong> of an environment for change rehearsal, then
cut-over or discard.</li>
</ul>
<p>On a physical network each of these is a project. On VCF with NSX VPCs it&rsquo;s
a template.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Overlapping <code>privateIPs</code> across VPCs is <strong>supported and intentional</strong>.
Identical pods are a feature, not a hack.</li>
<li>Fresh VPCs realize subnets <strong>deterministically in creation order</strong> —
fix the order (<code>dependsOn</code> in a blueprint) and every pod gets the same
map.</li>
<li>Pods are unreachable from outside by construction; publish exactly what
you mean to via <code>LoadBalancer</code> VIPs from the external block.</li>
<li>Prove isolation from a <em>different</em> VPC, with a positive control (own
VPC reachable) beside the negative.</li>
<li>Expect duplicate MACs across pods from appliance images. It&rsquo;s fine.</li>
</ul>
<p><em>Previously: <a href="/posts/the-lb-that-must-exist-first/">the LB that must exist first</a>.
Next: one WSUS for pods that can&rsquo;t see each other.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Output captured live, trimmed for length,
never edited for outcome.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
