VMware Cloud Foundation 9.1 · Nested Lab Recovery

Recovering an Interrupted VCF 9.1 Deployment After NFS Failure, Cloud Proxy Loss, and Stale Fleet State

A storage incident turned a normal second-VCF-instance deployment into a multi-layer recovery: Photon OS filesystem corruption, a missing Unified Cloud Proxy, stale Operations/Fleet inventory, orphaned SDDC Manager deployment state, a lost static route, and an admin@local credential lockout.

September 4, 2026 · Environment: two-site nested VCF 9.1 lab · Site B existing VCF instance + Site A new VCF instance

The lab and the failure domain

This incident happened while I was building a second VMware Cloud Foundation 9.1 instance in a nested two-site lab. Site B already had a working VCF instance and hosted the shared VCF Operations/Fleet services. Site A was being deployed as a separate VCF instance and was joining the existing Operations/Fleet environment.

Existing Operations
vcf-ops.home.local · Site B
Existing Site-B Cloud Proxy
vrops-c-b01.home.local · 10.20.10.60
New Site-A Cloud Proxy
vrops-c-a01.home.local · 10.10.10.60
Site-A SDDC Manager
vcfa-sddc.home.local · 10.10.10.167
VCF Installer
vcf-installer.home.local · 192.168.1.190
Lab router / DNS
192.168.1.90

The deploy had already survived the normal sharp edges of running VCF inside nested ESXi: VLAN tagging, management-vmk migration, vSAN, NSX sizing, DNS, and service-runtime placement. Then an NFS/storage interruption destabilized the lab. VMs were restarted, NSX briefly became unavailable, and the in-flight VCF deployment was left in a partially completed state.

This was not one single error.
The storage incident was the trigger, but recovery required fixing each layer independently: appliance filesystem health, Operations inventory, Fleet state, SDDC Manager state, Installer orchestration, routing, and finally credentials.

Table of contents

Before the storage interruption: nested VCF had already exposed several lab-specific traps

It is worth separating the storage incident from the earlier deployment problems. These were real blockers, but they had already been solved before the final Cloud Proxy/Fleet recovery.

1
Nested virtualization had to be exposed.
VCSA power-on initially failed until hardware-assisted virtualization was exposed to each nested ESXi VM.
2
Bootstrap VLAN tagging mattered.
The inner port group and the outer ESXi port group could not both tag VLAN 110. During bootstrap, the inner VM Network used VLAN 0 over an outer access VLAN; after migration to the vDS, the outer NIC had to move to the 4095 trunk.
3
Management vmk migration required the outer NIC to change at exactly the right time.
Once vmk0 moved to the vDS management port group on VLAN 110, the corresponding outer vNIC needed to move from A-MGMT to A-NSX-TRUNK.
4
NSX Manager placement needed enough virtual CPU.
The Medium NSX appliance required more compute than the first nested-host sizing provided, so the nested hosts were increased to 12 vCPU with substantially more RAM.
5
DNS had to be deterministic.
The Site-A SDDC Manager originally had both the lab DNS server and the upstream home router configured. The upstream router did not own the home.local records, producing intermittent UnknownHostException failures. SDDC Manager was corrected to use only 192.168.1.90.
One of the Fleet/VCF Management task views captured during the broader nested deployment troubleshooting.
One of the Fleet/VCF Management task views captured during the broader nested deployment troubleshooting.

Side incident: the existing Site-B Cloud Proxy would not boot

While the Site-A deployment was being recovered, the Cloud Proxy associated with the original VCF instance also failed after the storage disruption. VCF Operations showed the proxy offline.

The existing Site-B Unified Cloud Proxy was offline after the storage interruption.
The existing Site-B Unified Cloud Proxy was offline after the storage interruption.

The console made the cause much clearer:

[FAILED] Failed to start File System Check on /dev/disk/by-label/root
[DEPEND] Dependency failed for /sysroot
[DEPEND] Dependency failed for Initrd Root File System
[DEPEND] Dependency failed for Mounts Configured in the Real Root
The Site-B Cloud Proxy dropped into Photon OS emergency mode because the root filesystem could not be checked/mounted.
The Site-B Cloud Proxy dropped into Photon OS emergency mode because the root filesystem could not be checked/mounted.

Broadcom documents this class of Photon OS failure as filesystem corruption commonly following storage loss, power interruption, or an abrupt crash. The successful repair was to edit the GRUB boot entry, append fsck.repair=yes to the Linux line, and allow the appliance to repair the filesystem before continuing the boot.

# GRUB: append to the linux line
fsck.repair=yes
Result: the filesystem repair completed and the existing Site-B Cloud Proxy booted normally again. This proxy did not need to be redeployed.

If the automatic repair had failed, the next-level recovery would have been single-user/read-only boot and a targeted e2fsck against the actual root/boot partitions. I did not need that fallback in this incident.

The Site-A Cloud Proxy was a different problem: the VM was gone, but Fleet still remembered it

The new Site-A proxy was not simply offline. After the host/storage disruption, the VM that had been deployed during the VCF workflow was no longer present. The VCF Installer still believed it was in the middle of Join the existing operations appliance and sat at 4/7 tasks complete.

The VCF Installer stalled at 4/7 during the Operations join. The Cloud Proxy install and completion tasks never started.
The VCF Installer stalled at 4/7 during the Operations join. The Cloud Proxy install and completion tasks never started.

At first glance this looked like an inactive Fleet Lifecycle task. It was not. The more important clue was inventory: Fleet still contained a Site-A Cloud Proxy node even though the actual VM no longer existed.

On the Fleet Build service, the stale node had the Site-A IP 10.10.10.60. Earlier inventory also showed a confusing duplicate-name condition where the Site-B and Site-A proxy objects could both surface the Site-A display name. That made it especially important to identify proxies by UUID, IP address, and actual hostname rather than by friendly name alone.

Proving what VCF Operations actually knew

The source of truth check was the CASA Cloud Proxy inventory on the primary VCF Operations node:

curl --insecure -s -u "admin" \
  https://localhost/casa/onprem/v1/cloudproxies \
  | python -m json.tool

After deleting the stale Site-A proxy object from Operations and synchronizing inventory, CASA returned only the legitimate Site-B proxy:

UUID:     <site-b-proxy-uuid>
IP:       10.20.10.60
hostname: vrops-c-b01.home.local
role:     ONE_WAY_REMOTE_COLLECTOR
type:     UNIFIED_CLOUD_PROXY

DNS was also clean in both directions:

vrops-c-b01.home.local  -> 10.20.10.60
10.20.10.60             -> vrops-c-b01.home.local

vrops-c-a01.home.local  -> 10.10.10.60
10.10.10.60             -> vrops-c-a01.home.local
Why this mattered:
VCF 9.x converged Fleet management is driven by Operations/Fleet inventory. If that inventory says a Cloud Proxy already exists, the Installer has no reason to create another one. Cleaning stale inventory was required before redeployment could make sense.

Broadcom KB 447501 is useful here: deleting a Cloud Proxy in Operations does not necessarily remove its VM, and Fleet inventory can lag because of synchronization. The CASA endpoint is an excellent verification point.

Manually redeploying the Site-A Unified Cloud Proxy

Broadcom KB 438951 describes a closely related VCF 9.1 multi-instance failure: completing Fleet/Operations integration requires a healthy, configured Cloud Proxy, but a missing health precheck can let the workflow proceed too far. The documented remediation is to deploy a new Cloud Proxy from the VCF Operations UI.

I redeployed Site A with the intended identity:

FQDN:    vrops-c-a01.home.local
IP:      10.10.10.60
Gateway: 10.10.10.1
DNS:     192.168.1.90
VCF Operations Lifecycle now showed the new Site-A VCF instance alongside the original Site-B instance.
VCF Operations Lifecycle now showed the new Site-A VCF instance alongside the original Site-B instance.

At this point the new proxy existed and VCF Operations could see Site A — but the Installer did not advance.

Even with the Site-A Cloud Proxy deployed and the instance visible in Operations, the Installer remained stuck at 4/7 with zero active tasks.
Even with the Site-A Cloud Proxy deployed and the instance visible in Operations, the Installer remained stuck at 4/7 with zero active tasks.

The next layer: SDDC Manager still thought Operations and the Cloud Proxy were IN_PROGRESS

The Site-A SDDC Manager exposed the real inconsistency. Its vcf_management_component table showed the Services Runtime and SDDC Lifecycle as complete, but the existing Operations node and the new Cloud Proxy were still marked IN_PROGRESS:

SELECT component_type, fqdn, deployment_type, deployment_status
FROM vcf_management_component
ORDER BY component_type;

SDDC_LCM                    vcf-a-instance.home.local  NEW       SUCCEEDED
VCF_OPERATIONS_CLOUD_PROXY  vrops-c-a01.home.local    NEW       IN_PROGRESS
VCF_OPERATIONS:MASTER       vcf-ops.home.local         EXISTING  IN_PROGRESS
VSP                         vcf-a-msr.home.local       NEW       SUCCEEDED

The public API confirmed the same stale state:

GET /v1/vcf-management-components

vcfOperations.deploymentStatus          = IN_PROGRESS
vcfOperationsCollector.deploymentStatus = IN_PROGRESS
vspCluster.deploymentStatus              = SUCCEEDED
sddcLcm.deploymentStatus                 = SUCCEEDED

Meanwhile, /v1/vcf-management-components/tasks/latest did not return a populated active task. In other words, there was no real component task doing work anymore — only state left behind by the interrupted orchestration.

Lab-only state repair — not a Broadcom-documented production procedure.
In this nested lab I took a snapshot and made a surgical correction to the two orphaned rows. Broadcom documents other stale-management-component cleanup procedures, but I did not find vendor documentation authorizing this exact selective update. In production I would open a Broadcom support case before changing the platform database.
BEGIN;

UPDATE vcf_management_component
SET deployment_status = 'SUCCEEDED'
WHERE component_type IN (
    'VCF_OPERATIONS_CLOUD_PROXY',
    'VCF_OPERATIONS:MASTER'
)
AND deployment_status = 'IN_PROGRESS';

COMMIT;

The update changed exactly two rows, and the public API immediately reflected SUCCEEDED for both Operations and the Site-A collector. That confirmed the stale state had been in the database itself, not in a Commons Services cache.

The Installer still had a zombie parent workflow

Fixing component state did not automatically wake the top-level Installer execution. Restarting domainmanager on the VCF Installer caused it to clean up the old interrupted EXTEND_VCF execution, mark it failed, run the error handlers, and make the deployment retryable.

systemctl restart domainmanager
systemctl status domainmanager --no-pager

After the retry, the workflow finally started executing again — which was progress, because it exposed the next real problem instead of sitting indefinitely with no active subtasks.

The previously silent hang became a normal failed/retryable Installer state, allowing the parent SDDC workflow to run again.
The previously silent hang became a normal failed/retryable Installer state, allowing the parent SDDC workflow to run again.

An important lesson here was that a restart is not always “just a restart.” Domain Manager has startup cleanup behavior for interrupted orchestrator contexts. In this case that behavior was exactly what converted a zombie execution into a state the Installer could retry.

The retry exposed a networking regression: the Installer route was gone

The retried workflow immediately tried to collect the Site-A SDDC Manager SSH host key and timed out:

ssh: connect to host vcfa-sddc.home.local port 22: Connection timed out

DNS was fine:

getent hosts vcfa-sddc.home.local
10.10.10.167 vcfa-sddc.home.local

But the route was wrong:

ip route get 10.10.10.167

10.10.10.167 via 192.168.1.1 dev eth0 src 192.168.1.190

The VCF Installer had previously been given static routes through the lab router, but they were non-persistent and had disappeared. The correct next hop was 192.168.1.90:

ip route replace 10.10.0.0/16 via 192.168.1.90 dev eth0
ip route replace 10.20.0.0/16 via 192.168.1.90 dev eth0

After that change, the Installer could reach vcfa-sddc.home.local:22 and successfully collected the RSA/ECDSA SSH host keys. The ssh-dss and ssh-ed25519 negotiation messages in the debug log were harmless probes; supported keys had already been retrieved.

Postmortem action:
Routes this important to a VCF deployment must be persistent. A reboot or service recovery that silently removes them can turn later orchestration retries into misleading component failures.

The next retry failed on admin@local — because I had changed it during troubleshooting

Once routing worked, the Installer reached the Site-A SDDC Manager API and failed for a completely different reason:

FAILED_TO_GET_SDDC_MANAGER_TOKEN

POST https://vcfa-sddc.home.local/v1/tokens
401 IDENTITY_UNAUTHORIZED_ENTITY
User is not authorized

Earlier in the troubleshooting I had reset admin@local directly on Site-A SDDC Manager to regain API access. That fixed my manual token test, but it created a mismatch: the VCF Installer still had the original generated password in its deployment specification.

Broadcom KB 403099 documents the local password storage and reset mechanism:

mkdir -p /etc/security/local
chown root:vcf_services /etc/security/local
chmod 650 /etc/security/local

touch /etc/security/local/.localuserpasswd
chown root:vcf_services /etc/security/local/.localuserpasswd
chmod 660 /etc/security/local/.localuserpasswd

printf '%s' "$PASSWORD" \
  | openssl dgst -sha512 -binary \
  | openssl enc -base64 \
  | tr -d '\n' \
  > /etc/security/local/.localuserpasswd

The fix was to put admin@local back to the original value shown by the VCF Installer under Review Passwords, so the SDDC Manager and the Installer agreed again.

Repeated bad token attempts then produced:

HTTP 403
LOCAL_ACCOUNT_LOCKED
Local account is locked.

That was another valuable clue: the password mismatch was no longer theoretical. The old workflow had been hammering the SDDC Manager token endpoint with a stale credential. After stopping the retries, restoring the original password, and clearing the lockout/cooldown, token generation succeeded and the Installer could continue.

Once the state, route, and credential all matched, VCF resumed normally

The visible change was immediate: Join the existing operations appliance went from 4/7 to 7/7, and VCF Management Services started deploying.

The Operations join milestone finally completed. The workflow moved into VCF Management Services and started Telemetry deployment.
The Operations join milestone finally completed. The workflow moved into VCF Management Services and started Telemetry deployment.

Telemetry was installed through Fleet Lifecycle and reached SUCCEEDED. The Installer then created a new Fleet LCM task for Salt, polled it normally, and continued.

Telemetry was complete and the Salt Master Fleet Lifecycle task was actively progressing.
Telemetry was complete and the Salt Master Fleet Lifecycle task was actively progressing.

The occasional getSystemInfo errors seen in the debug log were not the blocker at this stage. The code fell back to discovering the local VSP cluster from Site-A SDDC Manager, found vcf-a-msr.home.local and vcf-a-instance.home.local, discovered the shared fleet-lcm service from VCF Operations, exchanged a session token, and continued.

At 04:23 UTC the Installer patched the Site-A management-component record for Salt to SUCCEEDED and the parent orchestration reached its end state.

Success: the second nested VCF 9.1 instance completed, including vSphere, NSX, VCF Management Platform, Operations join, and VCF Management Services.
Success: the second nested VCF 9.1 instance completed, including vSphere, NSX, VCF Management Platform, Operations join, and VCF Management Services.

What I learned from this recovery

Storage failures leave state behindA VM can recover while orchestration, inventory, or an appliance filesystem remains inconsistent. “Everything is powered on” is not the same as “VCF state is coherent.”
Different Cloud Proxies can fail differentlyThe existing Site-B proxy was repairable with fsck; the partially deployed Site-A proxy had to be removed from stale inventory and redeployed.
Follow the ownership chainInstaller → SDDC Manager → VCF Operations → Fleet Lifecycle → Services Runtime. A stuck UI can be caused by stale state several layers below it.
Verify the source of truthCASA, the public SDDC Manager API, Fleet Build logs, and the platform database told a much clearer story than the top-level progress bar.
Do not ignore routing on an applianceThe Installer was alive and DNS worked, but its route reverted to the home router instead of the lab router. A single missing route blocked SSH and API-driven recovery.
Credential recovery can create a second problemResetting admin@local restored manual API access but broke the Installer until the original deployment password was restored.

The recovery sequence that finally worked

1
Repair the existing Site-B Cloud Proxy filesystem with GRUB fsck.repair=yes.
2
Remove the stale Site-A Cloud Proxy object and verify CASA contains only the real Site-B proxy.
3
Trigger Operations/Fleet inventory synchronization.
4
Deploy a fresh Site-A Cloud Proxy from the VCF Operations UI.
5
Verify Site-A SDDC Manager management-component state.
6
In this lab only, reconcile the two orphaned IN_PROGRESS rows after confirming no active task existed.
7
Restart Installer Domain Manager so the interrupted parent orchestration becomes retryable.
8
Restore the Installer routes through 192.168.1.90.
9
Restore admin@local to the original Installer-generated password and clear the local-account lockout.
10
Retry and let Fleet Lifecycle finish Telemetry and Salt. Deployment completes.

Quick command reference

Cloud Proxy inventory on VCF Operations

curl --insecure -s -u "admin" \
  https://localhost/casa/onprem/v1/cloudproxies \
  | python -m json.tool

VCF management-component state on SDDC Manager

psql -h localhost -U postgres -d platform -c "
SELECT component_type, fqdn, deployment_type, deployment_status
FROM vcf_management_component
ORDER BY component_type;"

Create an SDDC Manager API token

curl -sk -X POST https://localhost/v1/tokens \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin@local","password":"<password>"}'

Read management-component state

curl -sk \
  -H "Authorization: Bearer $TOKEN" \
  https://localhost/v1/vcf-management-components | jq

Restore lab routes on the VCF Installer

ip route replace 10.10.0.0/16 via 192.168.1.90 dev eth0
ip route replace 10.20.0.0/16 via 192.168.1.90 dev eth0

ip route get 10.10.10.167
nc -vz -w 3 10.10.10.167 22

Watch the Installer/Fleet transitions

LOG=/var/log/vmware/vcf/domainmanager/domainmanager.log

tail -f "$LOG" | \
grep --line-buffered -Ei \
'Fleet LCM Task ID|componentType|deploymentStatus|SUCCEEDED|FAILED|FAILURE|End of Orchestration'

Broadcom references that were useful

These were the most relevant vendor references for the individual failure modes. The selective SQL state change described above is intentionally not presented as a Broadcom-supported procedure.

Full screenshot timeline

The main article uses the screenshots that best show the turning points. For completeness, the rest of the screenshots captured during this deployment troubleshooting thread are included below as a collapsible gallery.

Show all additional screenshots (33)

Final takeaway

The hardest part of this incident was that every fix revealed another valid failure underneath it. The Cloud Proxy really was missing. Fleet really did have stale inventory. SDDC Manager really did have orphaned IN_PROGRESS state. The Installer really had lost its route. And admin@local really was out of sync and eventually locked.

Once those layers were repaired in order, VCF did not need to be rebuilt from scratch. The same deployment execution recovered, completed the Operations join, installed the remaining management services through Fleet Lifecycle, and finished successfully.

The practical lesson:
With VCF 9.1, treat a failed deployment as a distributed state machine. Do not troubleshoot only the screen that is currently red. Identify which service owns the current state, verify its source of truth, repair only that layer, and let orchestration proceed to the next check.