, ,

Decommissioning TKGI and Ops Manager, and What Comes Next (TKGI to OpenShift Series, Part 26)

A ten step teardown runbook for retiring a TKGI estate after the OpenShift cutover, covering cluster deletion, the TKGI tile, Ops Manager, BOSH, and the NSX-T and vSphere objects that survive every documented step.

TKGI to OpenShift Series · Part 26 of 26
Key takeaways:
• Decommissioning is the one phase of this migration with no rollback, so it runs on a clock rather than on a ticket. My default is a ninety day quiet period between the last production cutover and the first destructive command.
• Order protects you. Final backup, then staging, then production, then the development cluster last as a tripwire, then the Tanzu Kubernetes Grid Integrated (TKGI) tile, then Ops Manager and the BOSH Director, then NSX-T and vSphere leftovers, then the subscription.
• Headline command, and it is unforgiving: tkgi delete-cluster prod-01 –wait. Run it before you have released PersistentVolumes carrying a Retain reclaim policy and you inherit orphan disks with no owner.
• Deleting the TKGI tile does not delete the storage. In our estate 41 detached First Class Disks totalling 2.1 TB survived every documented teardown step and came off by hand.
• One artifact outlives everything else: the Velero backup bucket, switched to read only, held for the length of your longest audit retention.
Who this is for: Platform engineers and VMware admins who have finished production cutover onto OpenShift Container Platform 4 (OCP) and are still paying for a TKGI estate that no longer serves traffic. Assumed starting point: every workload wave restored on OpenShift, DNS resolving to Routes, TKGI clusters powered on and idle. If traffic has not moved yet, go back to Part 23 on production cutover and finish that first.

Decommissioning is the only phase of this migration that cannot be undone, which makes it the only phase that should be deliberately slow. Everything up to this point was reversible by pointing a DNS record back at the old load balancer. Once tkgi delete-cluster returns, that cluster, its etcd, its node VMs and most of its NSX-T objects are gone, and your only route back is a restore into a platform you are actively dismantling. Twenty five parts of this series were about building something. This one is about proving you no longer need the thing you are about to destroy.

Teardown Order That Protects Your Rollback

A TKGI estate is a stack of dependencies, and each layer can only be removed by the layer above it. Clusters are BOSH deployments owned by the TKGI tile. Once you delete that tile, no supported path exists to remove a cluster that is still running, and you are left deleting VMs by hand in vCenter while NSX-T keeps every logical router the NSX Container Plugin (NCP) ever created. Ops Manager, in turn, owns the BOSH Director, so removing the Ops Manager VM before you have deleted the installation strands the Director with nothing to manage it. Work top down, and every step still has a supported command behind it. Work in any other order and the rest of the teardown becomes manual archaeology.

vCPU Reclaimed Per Teardown Stage Three cluster TKGI 1.18 estate on vSphere, 26 VMs and 142 vCPU in total Prod cluster, 11 VMs 78 Staging cluster, 7 VMs 34 Dev cluster, 4 VMs 16 TKGI control plane, 2 VMs 8 BOSH Director, 1 VM 4 Ops Manager, 1 VM 2 Excludes 2.1 TB of detached First Class Disks, which no teardown step released
Capacity returned at each teardown stage. Two thirds of the reclaim sits in a single cluster.

That chart is worth reading before you plan the sequence, because it tells you where the impatience will come from. Production alone accounts for 78 of the 142 vCPU, and finance will notice when it comes back. Everything below it is rounding error. There is no capacity argument for rushing the small stages, and there is a very good reason to leave the development cluster running long past the point where it feels justified. Four VMs and 16 vCPU is the cheapest insurance policy in this entire migration. It is also the only remaining place where a forgotten pipeline can fail loudly instead of silently.

Against the usual order: Most teardown plans start with development because it feels lowest risk. I do the opposite. Development is where CI jobs, developer kubeconfigs and half remembered scripts still point, so it is the cluster that generates useful signal when it stays alive. Staging is genuinely idle once cutover is done, so staging goes first, production second, and development last.

Preflight, Proving Nothing Still Depends on TKGI

Asking application teams whether they still use TKGI produces a confident yes from people who have not looked, and a confident no from people who have forgotten. Ask the platform instead. Three sources give an honest answer: Kubernetes audit logs for who is still calling the API server, NSX-T load balancer statistics for who is still hitting the old virtual servers, and your DNS resolver logs for who is still resolving the legacy hostnames. My bar for declaring a cluster quiet is thirty consecutive days with no non-monitoring API request and zero bytes through the ingress virtual servers. Anything short of that is a guess dressed up as a decision.

# Versions this runbook was written and tested against # TKGI 1.18, tkgi CLI 1.18, BOSH CLI 7.9, Ops Manager 3.0 # OCP 4.20.4, oc 4.20.4, OADP 1.5.0 on OpenShift, Velero 1.16 on TKGI # govc 0.46, NSX-T 4.1 tkgi clusters Name Plan Name UUID Status Action dev-01 small 3b1f0a2e-6c47-4d18-9f02-1a7e55d3c810 succeeded UPDATE staging-01 medium 8c44de91-2fb3-4a70-8ce1-7d90b4f6a233 succeeded UPDATE prod-01 large f207ab6d-91c5-4e2b-a3d7-06c8e1b4f592 succeeded UPDATE # Anything still publishing an ingress? Ask the cluster, not the app team. kubectl –context prod-01 get ingress –all-namespaces –no-headers | wc -l 7 # Who called the API server today, excluding the platform itself? kubectl –context prod-01 logs -n kube-system kube-apiserver-0 –since=24h | grep -v -E ‘system:|kube-probe’ | awk ‘{ print $8 }’ | sort -u | head system:serviceaccount:concourse-main:worker

Seven ingress objects on a cluster that supposedly serves nothing is exactly the result you want from preflight, because it is cheap to investigate now and expensive to investigate after deletion. In our estate six of those seven were stale definitions left behind by teams who had already moved to Routes, and the seventh was a monitoring endpoint that a partner had bookmarked. That last line of output is the more interesting one. A Concourse worker service account was still authenticating against production, which turned out to be a contract test suite nobody had reassigned. Fix that class of thing while the cluster exists. It is a ten minute change now and an incident later.

Steps 1 to 5, Draining and Deleting TKGI Clusters

flowchart TD
  A[Ninety day quiet period after cutover] --> B[Final Velero backup, long TTL]
  B --> C[Delete staging cluster]
  C --> D[Delete production cluster]
  D --> E[Delete development cluster last, the tripwire]
  E --> F[Export Ops Manager installation settings]
  F --> G[Delete TKGI tile and apply changes]
  G --> H[Delete BOSH Director and Ops Manager VM]
  H --> I[Sweep NSX T1 routers, IP blocks and VIPs]
  I --> J[Sweep orphan vSphere First Class Disks]
  J --> K[Release subscription, archive backup bucket]
Teardown order. Every arrow is one way, and the first four steps are the only ones with a rollback.

Steps 1 to 3 are preparation inside each cluster, and skipping any of them turns a clean deletion into a support case. PodDisruptionBudgets block the drain that BOSH performs as it removes worker VMs, so a single budget with minAvailable set to the full replica count will hang the delete indefinitely. PersistentVolumes carrying a Retain reclaim policy are worse, because deletion succeeds and quietly leaves the backing disk on the datastore with no Kubernetes object referencing it. Capture that list of volume names before you delete anything, because after the cluster is gone the only identifier left on the disk is a UUID.

# 1. Final Velero backup on TKGI. Ninety day TTL. This is your only rollback. velero backup create tkgi-final-prod-01 –include-namespaces payments,web,postgres –ttl 2160h0m0s –wait Backup request "tkgi-final-prod-01" submitted successfully. Waiting for backup to complete. You may safely press ctrl-c to stop waiting. …………….. Backup completed with status: Completed. # 2. Remove PodDisruptionBudgets so the BOSH drain cannot stall kubectl –context prod-01 delete pdb –all –all-namespaces poddisruptionbudget.policy "payments-api" deleted poddisruptionbudget.policy "postgres-primary" deleted # 3. Record every Retain volume BEFORE the cluster disappears kubectl –context prod-01 get pv –no-headers | awk ‘$4 == "Retain" { print $1, $6 }’ | tee retain-pvs-prod-01.txt pvc-8a1c9d33-4f2e-4b71-9c05-3ad7e1b28c64 postgres/data-postgres-0 pvc-c41b77a0-8d19-4a63-bb2f-91e0c7d4a115 payments/uploads … 41 lines total # 4. Delete staging first, then production tkgi delete-cluster staging-01 –wait tkgi delete-cluster prod-01 –wait Are you sure you want to delete cluster prod-01? (y/n): y Cluster prod-01 delete in progress

Step 5 is to leave dev-01 alone. Resist the tidiness instinct here. On our estate the development cluster stayed up for the full ninety days after production went, and it earned its keep in the first fortnight. That is also the point at which the happy path ends, because production did not delete cleanly. It sat in progress for two hours and eleven minutes, and the TKGI CLI told me nothing useful. BOSH did.

tkgi cluster prod-01 Name: prod-01 Last Action: DELETE Last Action State: in progress Last Action Desc: Instance deletion in progress # Two hours in. Ask the Director what it is actually waiting on. bosh -e tkgi tasks –recent=3 ID State Description 9412 processing delete deployment service-instance_f207ab6d 9410 done delete deployment service-instance_8c44de91 bosh -e tkgi task 9412 –debug | tail -4 DEBUG – NSXT: deleting logical router t1-prod-01-c9d4 DEBUG – NSXT: failed to delete logical router t1-prod-01-c9d4: Logical Router has 2 attached LogicalPorts ERROR – Action Failed get_task_status: nsx cleanup timeout after 3600s

That error is the single most common way a TKGI teardown stalls, and it is worth understanding rather than working around. NCP creates a tier 1 logical router per cluster and hangs the load balancer virtual servers for every LoadBalancer service off it. When ingress objects are deleted out of band, by a person tidying up or by a Velero restore that removed the service on the source side, NCP loses track of the virtual server but NSX-T keeps it attached. BOSH then cannot delete a router that still has ports on it, and it retries for an hour before giving up. Fix it in NSX-T Manager under Networking and then Load Balancing: find the virtual server and server pool bound to that tier 1 router, delete both, then rerun tkgi delete-cluster prod-01 –wait. Ours completed in nine minutes on the second attempt. Broadcom documents the same cleanup stall against Supervisor clusters, and the remedy is identical.

Steps 6 to 10, Ops Manager, BOSH and NSX-T Teardown

Before anything destructive at this layer, export the Ops Manager installation settings. That zip file is a complete record of network ranges, availability zone mappings, plan definitions, certificate subjects and tile configuration, and it is the only artifact that can answer an auditor asking what the old estate looked like eighteen months from now. It costs one command and a few megabytes. I have never regretted taking it, and I have twice regretted not taking it earlier. Store it outside the vSphere estate you are about to shrink, and never in the same bucket as your Velero backups.

# Credentials come from the environment. Never put them in a command line. export OM_USER=$(pass ops/opsman-user) export OM_PASSWORD=$(pass ops/opsman-password) # 6. Export installation settings, the audit artifact om –target https://opsman.lab.local –skip-ssl-validation –username $OM_USER –password $OM_PASSWORD export-installation –output-file tkgi-final-$(date +%F).zip exporting installation installation exported to tkgi-final-2026-08-16.zip # 7. Confirm nothing but the TKGI tile itself remains deployed bosh -e tkgi deployments Name Release(s) Stemcell(s) pivotal-container-service-a1b2c3d4e5f6 pks-api/1.18.2 ubuntu-jammy/1.842 1 deployments # 8. Delete the tile, then apply changes to make it real om –target https://opsman.lab.local –skip-ssl-validation –username $OM_USER –password $OM_PASSWORD delete-product –product-name pivotal-container-service –product-version 1.18.2 om … apply-changes

Step 9 removes Ops Manager itself, and the order inside this one step matters more than any other in the runbook. Delete the installation from the Ops Manager dashboard first, using Delete This Installation, and let it remove the BOSH Director and its VMs while something still owns them. Only then power off and delete the Ops Manager VM in vCenter. If you deployed the Director directly with bosh create-env rather than through Ops Manager, the equivalent is bosh delete-env against the same manifest and state file, which idempotently removes the VMs, disks and stemcells it created. Reverse this order and you get a Director VM that nothing manages, running BOSH agents that nothing talks to, which is a slow afternoon in vCenter with a naming convention as your only guide.

# 10. Sweep vSphere for storage that no teardown step released export GOVC_URL=https://vcenter.lab.local export GOVC_USERNAME=$(pass ops/vc-user) export GOVC_PASSWORD=$(pass ops/vc-password) govc disk.ls -ds vsanDatastore | wc -l 41 govc disk.ls -ds vsanDatastore -l | head -3 2e3f7a91-… pvc-8a1c9d33-4f2e-4b71-9c05-3ad7e1b28c64 50.0GB detached 6b14cc02-… pvc-c41b77a0-8d19-4a63-bb2f-91e0c7d4a115 100.0GB detached 9d80f4e7-… pvc-2f6a03bd-77c1-4e59-8a44-b5d2e0916f37 20.0GB detached # Cross check every UUID against retain-pvs-prod-01.txt from step 3, # then remove one at a time. There is no undo. govc disk.rm -ds vsanDatastore 2e3f7a91-…

Forty one detached disks totalling 2.1 TB is not an unusual result, and it is the reason step 3 exists. Every one of those disks was created by the vSphere Container Storage Interface (CSI) driver for a PersistentVolume with a Retain reclaim policy, which is precisely the policy a careful operator sets on database storage. Careful behaviour during the build becomes cleanup debt during the teardown. Match UUIDs against the file you saved, keep the ones backing anything you might still restore, and delete the rest deliberately rather than in a loop. NSX-T deserves the same treatment: check for tier 1 routers, IP blocks, SNAT pools and virtual servers whose names still carry a cluster UUID, and remove them only after confirming no other consumer shares the block.

Verification and Clean End State

Green here looks like a series of commands failing in the right way. A BOSH client that cannot find a Director, a vCenter search that returns nothing, an NSX-T query with a zero count. Run all of them, and run them from the same jump host your automation used, because a leftover credential in a CI secret store is its own kind of debt.

bosh -e tkgi deployments Fetching info: Performing request GET https://10.40.12.5:25555/info: Retry: Get dial tcp 10.40.12.5:25555: connect: no route to host govc find / -type m -name ‘*’ | grep -c -E ‘pks|service-instance|bosh’ 0 curl -s -k -u $NSX_USER:$NSX_PASSWORD https://nsxmgr.lab.local/policy/api/v1/infra/tier-1s | grep -c ‘t1-prod-01’ 0 govc disk.ls -ds vsanDatastore | wc -l 0 # And the only cluster still answering is the one you built oc get nodes –no-headers | wc -l 9

Rollback at this stage is no longer a platform operation, it is a data operation, and it only works if you kept the right things. Below is the artifact I would hand a colleague on their first decommission. It is short on purpose, and the only two rows people argue with are the backup bucket and the exported settings, both of which are cheap to keep and impossible to recreate.

ArtifactAction at teardownHold for
Velero backup bucket from the final TKGI backupsKeep, switch the bucket policy to read onlyLongest audit retention, minimum 12 months
Ops Manager exported installation settingsKeep, stored outside the vSphere estateLife of the audit trail
First Class Disks backing Retain volumesKeep detached, then remove with govc disk.rm90 days
Harbor registry and its image tagsKeep as an upstream mirror for the OpenShift registryUntil mirror parity is proven
TKGI cluster kubeconfigs and UAA client secretsDelete, and revoke in the secret storeNothing, these are live credentials
NSX-T tier 1 routers, IP blocks and virtual serversDelete after confirming no other consumerNothing
TKGI monitoring dashboards and alert rulesExport as JSON, then deleteLife of the audit trail
Stemcells, VM templates and the Ops Manager OVADeleteNothing
Keep or delete decision table. Print this one and work down it.
Field note: On my first decommission we deleted the development cluster first, because it felt safest. Nine days later a nightly Concourse pipeline nobody owned began failing, and it took two engineers three days to trace it back to contract tests that had been publishing against dev-01. On the second estate I reversed the order. Staging went first, production second, and development stayed alive for the full ninety days as a tripwire. Four pipelines and one Grafana datasource announced themselves in the first fortnight, all of them a ten minute fix while the cluster still existed. Verdict: delete staging first and development last, and never delete the cluster engineers can still log into until nothing has logged into it for thirty days.

Common Failures and Remediation

Symptom or errorCauseFix
Delete stuck in progress for hours, BOSH task logs NSXT: failed to delete logical router, Logical Router has 2 attached LogicalPortsNCP lost track of a load balancer virtual server still bound to the cluster tier 1 routerDelete the stale virtual server and server pool in NSX-T Manager under Networking and Load Balancing, then rerun tkgi delete-cluster
Cluster reports deleted but node VMs reappear in vCenterBOSH resurrector recreated instances mid deleteRun bosh -e tkgi update-resurrection off, remove the stray VMs, then retry the delete
41 detached First Class Disks remain after every cluster is gonePersistentVolumes with a Retain reclaim policy were never releasedMatch disk UUIDs against the PV export from step 3, then remove each with govc disk.rm
delete-product succeeds, apply-changes fails with installation was unsuccessfulAn errand or a second tile still references the TKGI service networkDeactivate the TKGI errands, clear the stale service network binding, then rerun apply-changes
Ops Manager VM removed but BOSH VMs still runningInstallation was never deleted, so nothing owns the DirectorRedeploy Ops Manager from the exported settings and delete properly, or remove orphan VMs by hand matching the vm- and bosh- name prefixes
Emergency restore from the final TKGI backup fails on OpenShift with pods is forbidden: unable to validate against any security context constraintrestricted-v2 rejects a workload that ran under a permissive PodSecurityPolicy on TKGIRehearse the restore during the quiet period and bind the correct SCC in advance, as covered in Part 7
Six failures worth recognising by their error text alone.

Ninety Day Rollback Window, Then Delete

My recommendation for this final phase is a ninety day quiet period, measured from the last production traffic shift rather than from the change ticket, with the development cluster left running for its whole length. Ninety days covers a monthly close, a quarterly report and at least one seasonal load pattern, which between them surface the dependencies that a fortnight never will. Against that, the carrying cost is four VMs and 16 vCPU. I would not run a shorter window on a regulated estate, and I would not run a longer one anywhere, because a cluster nobody patches becomes a liability faster than it becomes a safety net. TKGI reaches official End of Support in October 2027, so this window is not open indefinitely in any case.

What comes next depends on what pushed you here. If the driver was NSX 9.1 and VCF 9, the OpenShift landing is one of two valid answers and the other one is worth reading before you commit anything else to it, which is why the TKGI to VKS series exists alongside this one. If the driver was consolidation, the next milestone is fleet management with Red Hat Advanced Cluster Management rather than another cluster. And if the pressure now coming at your platform is GPU workloads and model serving, OpenShift AI is the natural next build, covered end to end in the Red Hat Gen AI guide. Whichever way you go, the platform you just built is the substrate for all three.

Your Monday action is small and it starts the clock. Run the ingress count and the API audit query from the preflight section against all three TKGI clusters, write down the date of the last non-monitoring request on each, and put ninety days from that date in the calendar as the teardown start. That single date turns decommissioning from an argument into a schedule. When it arrives, work down the ten steps above in order, and keep the bucket. Twenty six parts on, the whole series is collected in the TKGI to OpenShift complete guide, so start wherever your estate actually is.

TKGI to OpenShift Series · Part 26 of 26
« Previous: Part 25  |  Guide

References

About The Author


Discover more from Journal of Intelligent Infrastructure

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure

Subscribe now to keep reading and get access to the full archive.

Continue reading