When Broadcom absorbed VMware, Tanzu Application Service renewals stopped being a line item people ignored. That is the trigger. The destination is usually upstream Kubernetes. What follows is not a like-for-like swap, and pretending otherwise is how these projects fail. Start with what you are actually giving up.
What Cloud Foundry was doing for you
cf push is deceptively powerful. In one command, CF detected your language, ran a buildpack to produce a runnable droplet, scheduled it, wired up routing, bound services, streamed logs, and gave you health checks. The platform was opinionated on purpose: developers shipped code, the platform handled everything between code and running process.
Kubernetes hands all of that back to you. It schedules containers and not much more out of the box. Every convenience CF baked in becomes a component you choose, install, and operate. The migration is really a project to rebuild those conveniences on a flexible substrate.
Step one: containerize the apps
CF apps are not containers, they are droplets built by buildpacks. You have two clean paths to images:
- Paketo buildpacks. These are the spiritual successors to CF buildpacks and produce OCI images from source with no Dockerfile. Because they share heritage with CF buildpacks, your apps usually build with zero source changes.
pack build myapp --builder paketobuildpacks/builder-jammy-base
- Dockerfiles. More control, more maintenance. Worth it for apps with native dependencies or unusual runtimes where you want to pin the base image yourself.
Start with Paketo for the bulk of the estate and reserve Dockerfiles for the awkward 10 percent.
Step two: map the platform pieces
This is the bulk of the work. Each CF abstraction needs a Kubernetes equivalent:
cf pushmanifest becomes a Deployment plus a Service. App instances become replicas. Memory and disk quotas become resource requests and limits.- Routes become an Ingress (or Gateway API) backed by an ingress controller like NGINX or Contour. CF gave you a route with one flag; here you write Ingress rules and run the controller.
- Service brokers and
cf bind-servicebecome Helm charts or operators. A bound Postgres from the marketplace becomes the CloudNativePG operator or a managed cloud database referenced through a Secret. Service binding credentials that CF injected asVCAP_SERVICESnow come from mounted Secrets or the Service Binding spec. - Org and space structure becomes namespaces with RBAC and ResourceQuotas.
Step three: rebuild observability
CF’s Loggregator firehose gave you aggregated logs and metrics for free. Kubernetes does not. You install this stack:
- Logging: Fluent Bit shipping to Loki or Elasticsearch.
- Metrics: Prometheus scraping, Grafana for dashboards.
- App health: CF’s health checks become
livenessProbeandreadinessProbein the Deployment spec.
This is real work and real ongoing ops. Do not underestimate it; for many teams it is the single largest line item in the migration.
Migrate space by space
Treat each CF space as a migration unit. Stand up a matching namespace, port the apps in that space to Deployments, cut routing over at the DNS or Ingress layer, validate, then move to the next space. Running CF and Kubernetes side by side during this window is normal and expected. Keep the CF route live until the Kubernetes Ingress is proven, then flip DNS.
Validate each space before you flip DNS
Treat validation as the gate between building a namespace and moving traffic to it. For each space, confirm the ported apps start and pass their new liveness and readiness probes, that the Ingress resolves the same hostnames with working TLS, and that bound services connect through their new Secrets or Service Binding source rather than the old VCAP_SERVICES injection. Check that logs and metrics actually land in the stack you built, Fluent Bit into Loki or Elasticsearch, Prometheus and Grafana for metrics, so you are not blind after cutover. Run real traffic against the Kubernetes deployment while the Cloud Foundry route still serves production, compare behavior, and only then flip DNS at the Ingress layer. Keep the Cloud Foundry space intact until its Kubernetes replacement has held real traffic, so a regression is a DNS flip back rather than a rebuild.
Where Cloud Foundry still wins
For a lot of shops it still wins on the thing that matters most: developer velocity.
cf pushis unbeaten for getting code to production with zero Kubernetes knowledge. A developer who never learns what a Deployment, Service, or Ingress is can ship all day on CF. That productivity is real and you will spend it down during migration.- Lower ops burden. CF gave you logging, routing, and health checks as platform features. On Kubernetes those are your YAML and your Prometheus to maintain.
- Fewer moving parts to break. A managed CF foundation is one thing to operate. A Kubernetes platform is a dozen.
If the licensing pain is real but the velocity loss is unacceptable, look at Korifi, the CNCF project that reimplements the cf push experience on top of Kubernetes. Your developers keep the CF CLI and workflow; you run Kubernetes underneath. It is a genuine bridge that lets you decouple the “leave Broadcom” decision from the “retrain every developer” decision.
Before you commit
Cloud Foundry to Kubernetes is not a migration, it is a platform rebuild: containerize with Paketo, translate spaces to namespaces, brokers to operators, routes to Ingress, and rebuild the logging and metrics CF gave you for free. The licensing savings are real, but so is the ops burden you are taking on and the developer velocity you are spending. Weigh Korifi as a softer path, and model the platform-team headcount and observability stack in the calculator above before you commit, because that hidden cost is what decides whether this pencils out.