<?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>Ncp on The Nested Lab</title>
    <link>https://thenestedlab.com/tags/ncp/</link>
    <description>Recent content in Ncp 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/ncp/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>
  </channel>
</rss>
