<?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>Troubleshooting on The Nested Lab</title>
    <link>https://thenestedlab.com/tags/troubleshooting/</link>
    <description>Recent content in Troubleshooting on The Nested Lab</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <lastBuildDate>Wed, 16 Sep 2026 08:10:00 +0100</lastBuildDate>
    <atom:link href="https://thenestedlab.com/tags/troubleshooting/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>The load balancer that must exist before the namespace</title>
      <link>https://thenestedlab.com/posts/the-lb-that-must-exist-first/</link>
      <pubDate>Wed, 16 Sep 2026 08:10:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/the-lb-that-must-exist-first/</guid>
      <description>VIPs pending forever, a retryable error that never stops retrying, and an ordering rule the docs don&amp;rsquo;t tell you: in a self-service NSX VPC, the LBService must exist before the namespace that will use it.</description>
      <content:encoded><![CDATA[<p>Everything was green. The VPC: realized. The namespace: ready. The VMs:
powered on, endpoints populated, ports listening. And the LoadBalancer
services sat at <code>&lt;pending&gt;</code> — for an hour.</p>
<p>This is the story of the least helpful error message in my recent memory,
what it actually means, and the one-line ordering rule that would have saved
an afternoon. If you&rsquo;re doing self-service NSX VPCs on VCF 9 with the
vSphere Supervisor, you will hit this. Bookmark accordingly.</p>
<h2 id="the-setup">The setup</h2>
<p>Tenant-created VPC (via the VCF Automation CCI API), a supervisor namespace
pinned to it, and a couple of <code>VirtualMachineService</code> objects of type
<code>LoadBalancer</code> to publish SSH and HTTPS for the workloads inside. Standard
stuff — the exact pattern that works out of the box in the org&rsquo;s default VPC.</p>
<p>The k8s side looked perfect:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ kubectl get endpoints -n pod-a
</span></span><span class="line"><span class="cl">NAME           ENDPOINTS                        AGE
</span></span><span class="line"><span class="cl">esx01-access   172.30.0.40:443,172.30.0.40:22   6m36s
</span></span><span class="line"><span class="cl">esx02-access   172.30.0.41:443,172.30.0.41:22   6m35s
</span></span></code></pre></div><p>Endpoints resolved. VIPs: nothing. The only clue, a recurring event:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Warning  FailedRealizeNSXResource  service/esx01-access
</span></span><span class="line"><span class="cl">Generic error occurred during realizing network for Service
</span></span></code></pre></div><p>&ldquo;Generic error.&rdquo; Wonderful.</p>
<h2 id="digging-what-ncp-actually-wants">Digging: what NCP actually wants</h2>
<p>The supervisor&rsquo;s network container plugin (NCP) logs told the real story:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">nsx_ujo.ncp.nsx.policy.lb_layer4_service Lb Service not Found for Namespace pod-a
</span></span><span class="line"><span class="cl">NCP00270 Failed to process virtual ip for service ...: Lbs pod-a is not found
</span></span><span class="line"><span class="cl">Encountered retryable error ... : Lbs pod-a is not found
</span></span></code></pre></div><p>NCP wants an NSX <strong>LBService</strong> in the namespace&rsquo;s VPC. In the org&rsquo;s
<em>default</em> VPC, one exists — the platform created it when the VPC was born.
In my self-service VPC? Nobody had created one. Fair enough — that&rsquo;s
actually documented behaviour once you know where to look: a fresh VPC needs
a <code>LoadBalancer</code> object (and before that, a <code>VPCAttachment</code> to a
connectivity profile with the service gateway enabled, or the LB creation
itself fails with a much better error message).</p>
<p>So I created the attachment, then the LBService. NSX: <code>Realized=True</code>.
Problem solved?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Warning  FailedRealizeNSXResource  service/esx01-access
</span></span><span class="line"><span class="cl">Generic error occurred during realizing network for Service
</span></span></code></pre></div><p>No.</p>
<h2 id="the-actual-bug-shaped-behaviour-a-snapshot-not-a-lookup">The actual bug-shaped behaviour: a snapshot, not a lookup</h2>
<p>Here&rsquo;s the part that costs you the afternoon. That &ldquo;retryable error&rdquo; retries
the <em>lookup in NCP&rsquo;s cache</em> — not the discovery. <strong>NCP snapshots the VPC&rsquo;s
LB inventory when the namespace is created.</strong> An LBService that appears
afterwards is never discovered, no matter how long you wait:</p>
<ul>
<li>Recreating the k8s services: no effect.</li>
<li>Tagging the LBService with the <code>nsx-op/*</code> ownership tags the working ones
carry: no effect — the cache doesn&rsquo;t re-read NSX.</li>
<li>Restarting NCP would force a full resync — but supervisor system pods are
protected; even <code>Administrator@vsphere.local</code> gets a Forbidden.</li>
<li>Mutating the namespace to nudge a re-sync: also blocked, by the
supervisor&rsquo;s namespace validation webhook.</li>
</ul>
<p>As a tenant, there is exactly one fix: <strong>delete and recreate the namespace</strong>,
now that its VPC has an LB. Fifteen minutes of rebuild for want of one
ordering rule.</p>
<p>And the control experiment proves the rule: a namespace created <em>after</em> its
VPC already had an LBService got its VIPs assigned without any drama —</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">esx01-access   VIP=192.168.144.34   22 OPEN · 443 OPEN
</span></span><span class="line"><span class="cl">esx02-access   VIP=192.168.144.35   22 OPEN · 443 OPEN
</span></span></code></pre></div><p>And once the ordering is right, this is what &ldquo;working&rdquo; looks like — the
pod&rsquo;s state a couple of minutes after a correctly-ordered deployment:</p>
<p><img alt="Live replay: catalog-deployed pod with both VMs powered on and VIPs assigned" loading="lazy" src="/images/c2-catalog-pod.gif"></p>
<p><img alt="VCFA deployment topology: namespace, subnets, hosts, two VIPs" loading="lazy" src="/images/ui/u4-deployment-topology.jpg">
<em>What the requester sees once the order is right.</em></p>
<h2 id="the-ordering-rule">The ordering rule</h2>
<p>For every self-service VPC that will publish LoadBalancer services, create —
in this order, <em>before</em> the namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">1. VPC                                   (vpc.nsx.vmware.com/v1alpha1)
</span></span><span class="line"><span class="cl">2. VPCAttachment                         (connectivity profile w/ service gateway
</span></span><span class="line"><span class="cl">                                          — LB creation errors without it)
</span></span><span class="line"><span class="cl">3. LoadBalancer   {regionName, vpcName}  (the step everyone misses)
</span></span><span class="line"><span class="cl">4. ...and only THEN the Supervisor Namespace
</span></span></code></pre></div><p>Encode it in whatever provisions your VPCs — a script, a pipeline, an
operator. It&rsquo;s four API calls and it turns a silent, undiagnosable
<code>&lt;pending&gt;</code> into a platform that just works.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>Nobody buys a platform for its ordering rules — but this is exactly the kind
of edge that decides whether self-service provisioning feels reliable or
flaky to the people using it. In a customer deployment the answer isn&rsquo;t a
blog post; it&rsquo;s that the provisioning automation already does the four
steps in the right order, every time, so a tenant never sees a VIP stuck at
<code>&lt;pending&gt;</code>. Knowing where the sharp edges are — because you&rsquo;ve been cut by
them in a lab — is most of what an experienced delivery partner is for.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>In a self-service NSX VPC, <strong>the LBService must predate the namespace</strong>.
NCP discovers LBs at namespace-add and never again.</li>
<li><code>FailedRealizeNSXResource: Generic error</code> on a Service = go read the NCP
logs; the real message (<code>Lbs &lt;ns&gt; is not found</code>, NCP00270) is there.</li>
<li><code>VPCAttachment</code> (service gateway) is the prerequisite for the LB itself —
that one at least fails loudly.</li>
<li>Retro-tagging NSX objects to look &ldquo;owned&rdquo; doesn&rsquo;t help a cache that never
re-reads. Recreating the namespace is the only tenant-level fix.</li>
<li>While you&rsquo;re at it: new namespaces also reject VM creation until image
<code>status.disks</code> syncs (~1–3 minutes after content library attach). Build
the wait into your automation and both sharp edges disappear.</li>
</ul>
<p><em>Previously in this series: <a href="/posts/nested-esxi-nsx-vpc/">nested ESXi inside an NSX VPC</a>.
Next: three datacenters, one IP plan — identical isolated pods.</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>
    <item>
      <title>Blueprinting the supervisor: seven CCI blueprint gotchas</title>
      <link>https://thenestedlab.com/posts/cci-blueprint-gotchas/</link>
      <pubDate>Wed, 16 Sep 2026 07:10:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/cci-blueprint-gotchas/</guid>
      <description>Everything that made the nested-esxi-pod blueprint fail validation before it worked: ${input} inside flow mappings, name vs generateName, flat zones, contentSources, one-published-version, validation-in-status, and the image-sync race. Short, specific, and each one cost me a cycle.</description>
      <content:encoded><![CDATA[<p>The <a href="/posts/nested-esxi-via-vcfa-all-apps/">nested-esxi-pod blueprint</a>
works. Getting there took seven distinct &ldquo;ContentValid: False&rdquo; (or worse:
a 200 that quietly did nothing). None of them are in the docs I could
find; all of them are five-minute fixes once you know. Here they are, in
the order they bit.</p>
<h2 id="1-inputx-is-illegal-inside-a-flow-mapping">1. <code>${input.x}</code> is illegal inside a flow mapping</h2>
<p>This looks like valid YAML and valid blueprint syntax:</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">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.hostname, value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;esx01.${input.podName}.res.lab&#34;</span>}}<span class="w">
</span></span></span></code></pre></div><p>It fails content validation. The expression parser doesn&rsquo;t reach into
flow-style (<code>{...}</code>) mappings. Block style is fine:</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">key</span><span class="p">:</span><span class="w"> </span><span class="l">guestinfo.hostname</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">value</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">esx01.${input.podName}.res.lab</span><span class="w">
</span></span></span></code></pre></div><p>Mixed style in the same list is fine too — only the entries that carry an
expression need to be block-style. (This is why the blueprint&rsquo;s
<code>vAppConfig</code> list looks inconsistent; it&rsquo;s deliberate.)</p>
<h2 id="2-name-vs-generatename-for-a-new-namespace">2. <code>name</code> vs <code>generateName</code> for a new namespace</h2>
<p>A <code>CCI.Supervisor.Namespace</code> you&rsquo;re <em>creating</em> must use <code>generateName</code>.
<code>metadata.name</code> is rejected by the CCI API — the platform appends a random
suffix, so <code>pod-a-</code> becomes <code>pod-a-dgf5p</code>. Everything downstream should
reference <code>${resource.namespace.id}</code>, never a literal name.</p>
<h2 id="3-zones-and-storage-classes-are-flat">3. Zones and storage classes are flat</h2>
<p>Early attempts wrapped them the way the raw CCI API does:</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">initialClassConfigOverrides</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">zones</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">...]</span><span class="w">
</span></span></span></code></pre></div><p>In a blueprint they&rsquo;re top-level properties of the namespace resource:</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">zones</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">domain-c9</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">cpuLimit</span><span class="p">:</span><span class="w"> </span><span class="l">40000M</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">memoryLimit</span><span class="p">:</span><span class="w"> </span><span class="l">64000Mi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">storageClasses</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vSAN Default Storage Policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">limit</span><span class="p">:</span><span class="w"> </span><span class="l">400000Mi</span><span class="w">
</span></span></span></code></pre></div><p>And zones are <strong>required</strong> — omit them and the API says &ldquo;Zone should be
specified&rdquo;, which at least is a clear message.</p>
<h2 id="4-a-new-namespace-has-no-content-library">4. A new namespace has no content library</h2>
<p>Deploy the namespace, deploy a VM, and get: no <code>VirtualMachineImage</code>
found. A VCFA-created namespace attaches <strong>no</strong> content libraries by
default. The fix is one block:</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">contentSources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">ISO, type</span><span class="p">:</span><span class="w"> </span><span class="l">ContentLibrary}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">f06-vks-lib01, type</span><span class="p">:</span><span class="w"> </span><span class="l">ContentLibrary}</span><span class="w">
</span></span></span></code></pre></div><p>Without it you&rsquo;re in the vSphere Client attaching libraries to a namespace
by hand, which rather defeats the catalog.</p>
<h2 id="5-images-sync-after-attach--wait-for-statusdisks">5. Images sync <em>after</em> attach — wait for <code>status.disks</code></h2>
<p>Even with libraries attached at creation, the first VM create in a fresh
namespace can be rejected:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">no disks found in image ... status.disks
</span></span></code></pre></div><p>The image objects appear immediately; their disk metadata syncs over the
next 1–3 minutes. The quota webhook checks <code>status.disks</code> and refuses
until it&rsquo;s populated. In a blueprint, put the hosts <code>dependsOn</code> something
that takes a couple of minutes (the binding maps did the job here), or add
an explicit wait. In a script, poll:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">kubectl get virtualmachineimage -n &lt;ns&gt; &lt;vmi&gt; -o jsonpath=&#39;{.status.disks}&#39;
</span></span></code></pre></div><h2 id="6-validation-lives-in-status-not-the-http-code">6. Validation lives in <code>status</code>, not the HTTP code</h2>
<p>Creating a <code>BlueprintVersion</code> returns 200 whether or not the content is
valid. Read the object back:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">status:
</span></span><span class="line"><span class="cl">  contentValid: false
</span></span><span class="line"><span class="cl">  validationMessages:
</span></span><span class="line"><span class="cl">    - &#34;... unexpected token ...&#34;
</span></span></code></pre></div><p>If your pipeline checks the response code, it will happily publish a
broken blueprint. Check <code>status.contentValid</code> and print the messages.</p>
<h2 id="7-only-one-published-version--409-on-the-second">7. Only one published version — 409 on the second</h2>
<p>Release 1.1.0 while 1.0.0 is released and you get a 409. It isn&rsquo;t a
transient conflict; it&rsquo;s the rule. <strong>Unrelease</strong> the current version, then
release the new one. Practically that means a publish step is
<code>unrelease old → release new</code>, and there&rsquo;s a short window where the
catalog item has no released version. Do it when nobody&rsquo;s requesting.</p>
<h2 id="bonus-the-things-that-arent-blueprint-problems">Bonus: the things that aren&rsquo;t blueprint problems</h2>
<p>Three prerequisites have <strong>no blueprint resource type</strong> and have to exist
before the request — VPC, VPCAttachment, LoadBalancer, <a href="/posts/the-lb-that-must-exist-first/">in that
order</a>. The blueprint&rsquo;s <code>vpcName</code>
input says &ldquo;must exist and be Realized&rdquo;, and it means it. Nothing in the
blueprint fails if they&rsquo;re missing; the deployment just never gets a VIP.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>VCF Automation&rsquo;s All Apps model is new, and new platforms have edges. None
of these seven are documented; all of them stall a first project by days if
you meet them cold. The value of a delivery partner who has already built
on the platform isn&rsquo;t the YAML — it&rsquo;s that a customer&rsquo;s first blueprint
publishes on day one instead of week two, and that the sharp edges are
encoded into templates and provisioning scripts where users never meet
them.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Expressions need <strong>block-style YAML</strong>; flow mappings don&rsquo;t get parsed.</li>
<li><code>generateName</code>, and reference the namespace by <code>${resource.x.id}</code>.</li>
<li><code>zones</code> and <code>storageClasses</code> are <strong>flat</strong> and zones are required.</li>
<li><code>contentSources</code> on the namespace, or nothing can be deployed.</li>
<li>Wait for image <code>status.disks</code> before the first VM (1–3 min).</li>
<li>Check <code>status.contentValid</code> — the HTTP code lies by omission.</li>
<li>One released version per blueprint: unrelease, then release.</li>
</ul>
<p><em>Companion to <a href="/posts/nested-esxi-via-vcfa-all-apps/">a datacenter in a catalog tile</a>.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Error text captured live.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
