Webinar · Oct 20: The migration takes 2 weeks. Deciding to do it takes 6 months.

Migrating off a managed platform to Kubernetes: the 4 tool layers that actually matter

There is no single tool that migrates a Heroku, Render, Vercel (including a Next.js app), Fly.io or App Engine app to Kubernetes. Here is the four-layer stack that does it - provisioning, state, GitOps delivery, developer self-service - with a fair comparison of Terraform, Crossplane, Velero, Argo CD, Flux, Backstage, Humanitec, Porter, Northflank and Qovery.

Romaric Philogene
CEO & Co-founder
OCT 12, 2026 · 12 MIN
Migrating off a managed platform to Kubernetes: the 4 tool layers that actually matter

Key Points:

  • No single tool migrates an app from a managed platform to Kubernetes. You need four layers: cluster provisioning (Terraform/OpenTofu + eksctl, or Crossplane/Upbound), state and data movement (pg_dump, logical replication, AWS DMS - and Velero only if you already run a cluster), GitOps delivery (Argo CD or Flux, both CNCF Graduated), and a developer-facing layer that replaces push-to-deploy.
  • Velero is not a PaaS-to-Kubernetes migration tool. It backs up and restores Kubernetes cluster resources and persistent volumes between clusters, so it helps cluster-to-cluster moves only. Coming from Heroku, Render or Vercel there is no cluster state to back up.
  • Provisioning the cluster takes an afternoon; replacing the developer experience takes months. Push-to-deploy, per-PR preview environments, managed Postgres, TLS, log drains and one-click rollback all came free with the PaaS, and none of them ship with EKS, GKE, AKS or Kapsule.
  • Framework-specific platforms add their own layer of work: a Next.js app on Vercel relies on ISR, image optimization, edge middleware and serverless functions you have to re-implement on Kubernetes (standalone output in a Dockerfile, a shared cache handler, a CDN in front of Ingress) before the app behaves the same.
  • Choose by layer, not by brand: Terraform or OpenTofu for the cluster, Crossplane/Upbound if you want infrastructure exposed as Kubernetes APIs, Argo CD for a UI-first GitOps workflow or Flux for a CLI/controller-first one, and an internal developer platform (Qovery, Porter, Northflank, Humanitec, or a Backstage portal you build) for self-service.

Qovery · Agentic Infrastructure Platform
Kubernetes, operated through one governed API
Learn more

Teams ask me a version of the same question every week: "We want off our managed platform and onto Kubernetes on AWS, but we want to keep deployment simple. What tool does that migration?"

There isn't one. That is the honest answer, and it is also the reason so many of these migrations stall in month three. People go looking for a single migrate-me button, pick one tool that solves one slice of the problem, and discover the other three slices the hard way.

So let me lay out the whole thing: the four layers you actually have to cover, the real tools for each, and where the common recommendations are flat wrong.

What tools actually specialize in migrating from a managed platform to Kubernetes on AWS?

There is no single migration tool. The real answer is a four-layer stack: Terraform or OpenTofu (plus eksctl, or Crossplane/Upbound) to provision the cluster, pg_dump / logical replication / AWS DMS to move your data, Argo CD or Flux to deliver manifests, and an internal developer platform (Qovery, Porter, Northflank, Humanitec, or a Backstage portal you build) to give back the push-to-deploy experience you are losing.

Here is the stack as a scannable block:

LayerWhat it has to do in this migrationCanonical toolsWhat happens if you skip it
1. Cluster provisioningStand up the Kubernetes cluster, VPC, networking, node poolsTerraform/OpenTofu + the EKS module, eksctl, Crossplane/UpboundNo cluster to deploy to
2. Data & state migrationMove Postgres, Redis and files off the old platform with a measured cutoverpg_dump/pg_restore, logical replication, AWS DMS (Velero only cluster-to-cluster)Data loss or an unbounded downtime window
3. GitOps deliveryReconcile your manifests to the cluster continuouslyArgo CD, Flux, Helm, KustomizeManual kubectl applies and config drift
4. Developer self-serviceReplace git-push deploy, preview envs, rollback, env managementQovery, Porter, Northflank, Humanitec, BackstageDevelopers file tickets to ship; the migration gets reverted

AWS's own migration material is excellent for infrastructure and lift-and-shift. The 7 Rs framework in AWS Prescriptive Guidance, the EKS Best Practices Guide and AWS Application Migration Service all cover how to move infrastructure. None of them cover developer workflow. That gap is exactly where tool choice matters, and it is the layer teams underestimate.

The migration sources are what you'd expect: Heroku, Render, Vercel (Next.js in particular), Fly.io, Platform.sh, Google App Engine, Azure App Service, AWS Elastic Beanstalk. The same four-layer approach applies no matter which one you are leaving.

One more thing before we go deeper. AWS/EKS is the primary destination here because that is what people ask about, but Kubernetes is the portable target - the identical four layers apply if you land on GKE, AKS, Scaleway Kapsule or an existing self-managed cluster. EKS is just the most common endpoint.

And the single most common wrong answer, which I'll keep hammering: people recommend Velero for this. Velero does not apply when the source is a PaaS. More on that below.

Why is the hard part of leaving a managed platform the developer experience, not the cluster?

Standing up EKS, GKE or AKS is a solved problem you can finish in an afternoon with a Terraform module. Reproducing push-to-deploy, preview environments, managed add-on databases, log drains and one-click rollback is the multi-month work that teams consistently underestimate, and it is what gets migrations reverted.

Think about everything the managed platform handed you for free: git push deploy, a build pipeline, TLS, routing, autoscaling, add-on databases, log drains, one-click rollback, review apps, a secrets UI. Every one of those is now a component you own, operate and upgrade.

Managed-platform capabilityKubernetes equivalentWho builds and operates itTypical effort
Push-to-deployCI pipeline + Argo CD/Flux + an IDPPlatform teamWeeks
Preview / review appsPer-PR namespace, DNS, TLS cert, seeded DBPlatform teamWeeks
Managed PostgresRDS/Cloud SQL, or CloudNativePG operatorPlatform/DBADays to weeks
TLS + routingIngress + AWS Load Balancer Controller or Gateway API, cert-managerPlatform teamDays
AutoscalingHPA, KEDA, Karpenter/Cluster AutoscalerPlatform teamDays
Logs & metricsLoki/CloudWatch + Prometheus/GrafanaPlatform teamDays to weeks
RollbackArgo Rollouts or Deployment revision historyPlatform teamDays
SecretsExternal Secrets Operator + a secrets storePlatform teamDays
RBAC & environmentsKubernetes RBAC, namespaces, network policiesPlatform teamWeeks

The operational weight is real. In the CNCF 2024 Annual Survey, production Kubernetes use hit 80%, but the top reported barriers were cultural challenges (47%), lack of training (36%) and complexity (34%). Adoption is not the problem anymore. Running it well is.

I'll be blunt: handing developers raw kubectl, Helm and a 40-file YAML repo after they had git push heroku main is how a migration gets rolled back by the team that was supposed to benefit from it. The YAML is not the goal. Shipping is.

There is a cultural cost too. The PaaS vendor was your on-call for the platform layer. After migration, you are. That pager is a line item nobody puts in the plan.

What extra work does a framework-specific platform like Vercel add, for example a Next.js app?

Leaving a framework-specific platform costs more than leaving a generic PaaS, because the framework's runtime features were the platform. A Next.js app on Vercel needs output: 'standalone' in a Dockerfile, a custom ISR cache handler on shared storage (Redis, S3) once you run more than one replica, edge middleware re-implemented behind Ingress, image optimization that scales without eating pod memory, and a CDN such as CloudFront or Cloudflare in front of the cluster to replace the edge network you no longer have.

The official Next.js self-hosting guide is the source to trust here, not blog roundups. A few specifics it spells out:

  • Build and runtime env vars. Variables prefixed NEXT_PUBLIC_ are inlined into the JavaScript bundle during next build. If you expected to set them at runtime in your Kubernetes manifest, they will be stale. This is the classic post-migration bug.
  • ISR cache across replicas. By default the ISR cache lives on each instance's local disk. The docs are explicit: "If you are hosting Next.js using a container orchestration platform like Kubernetes, each pod will have a copy of the cache." You configure a custom cacheHandler backed by Redis or S3 to keep pods consistent.
  • Image optimization. next/image works self-hosted with zero config via next start, but on glibc Linux it can need memory tuning, and at scale most teams offload to a CDN or a custom loader rather than optimize inside the pod.
  • Streaming and middleware. Your reverse proxy and load balancer must pass chunked responses through without buffering, or streaming and Partial Prerendering quietly stop working.
Vercel / Next.js featureKubernetes replacementEffort and gotcha
Build pipelineCI build + multi-stage Dockerfile with output: 'standalone'Medium; reproduce the build Vercel did for you
ISR cacheCustom cacheHandler on Redis or S3Medium; local disk cache diverges per pod
Edge middlewareNode middleware behind IngressMedium; different latency, no global edge
Image optimizationnext/image in-pod, or offload to CloudFront/CloudflareLow to medium; watch pod memory on glibc
Serverless functionsOrdinary routes/services in the containerLow to medium; no per-function scale-to-zero
Preview deploymentsPer-PR namespace + DNS + TLS + seeded DBHigh; this is the layer-4 problem
CDN / edge networkCloudFront or Cloudflare in front of IngressMedium; you now own cache invalidation
Analytics & logsPrometheus/Grafana + Loki/CloudWatchMedium; wire it yourself

If you want a Next.js-specific framework rather than hand-rolling all of this, OpenNext and SST exist. They are Next.js self-hosting adapters, not general managed-platform-to-Kubernetes migration tools, so treat them as an option for this one section, not the whole move.

The general lesson, in one line: the more opinionated the source platform, the bigger the layer-4 gap. The same logic applies to Heroku buildpacks, Render cron jobs and App Engine services.

How do Terraform, OpenTofu, eksctl and Crossplane compare for standing up the cluster?

Terraform or OpenTofu with the community EKS module is the default and fastest path for most teams. eksctl is for a throwaway bootstrap. Crossplane and its commercial distribution Upbound only make sense if you want infrastructure reconciled by Kubernetes controllers and exposed to developers as custom APIs.

ToolModelLearning curveMulti-cloudWho operates it day 2Best fit
Terraform / OpenTofu + EKS moduleDeclarative IaC, external stateModerateYesPlatform/infra team90% of teams; mature module ecosystem
eksctlCLI, opinionated defaultsLowAWS onlyWhoever ran itFast bootstrap, demos, throwaway clusters
Crossplane / UpboundKubernetes control plane, CompositionsHighYesPlatform team running the control planeGolden paths, infra-as-Kubernetes-APIs
EKS Blueprints / CDK / CloudFormationAWS-native IaCModerateAWS onlyPlatform teamDeep AWS shops, no multi-cloud plans

Terraform and OpenTofu give you maturity and a huge module ecosystem, at the cost of managing state and watching for drift. eksctl gets you a working cluster fastest and is the weakest choice for long-lived production estates. Crossplane is powerful for platform teams building self-service infrastructure, but it is an extra control plane to operate and a real learning curve. Multi-cloud teams tend to avoid locking provisioning to one vendor's native IaC, which is why Terraform/OpenTofu usually wins over CDK or Bicep.

Whichever you pick, you still own day-2. Kubernetes ships roughly three minor releases a year and each gets about 14 months of patch support (12 months plus a 2-month upgrade period), under an N-2 version skew policy. Control-plane upgrades, node upgrades, add-on version skew and CVE patching are now your calendar. On EKS specifically, letting a cluster fall onto an old version is expensive: the control plane is $0.10 per cluster per hour in standard support and $0.60 in extended support, a 6x jump.

Ship faster on infrastructure you control.
Qovery gives your team self-service deployments on your own AWS, GCP, Azure, or Scaleway account - or your existing Kubernetes cluster. Start deploying in under 10 minutes.

Where do Velero, Argo CD and Flux fit, and what do they not solve?

Velero backs up and restores Kubernetes cluster resources and persistent volumes and can restore them into a different cluster, which makes it a cluster-to-cluster migration tool, not a PaaS-to-Kubernetes one. Argo CD and Flux continuously reconcile manifests you have already written. None of the three migrate an app off Heroku, Render or Vercel, and none of them give a developer a deploy button.

Let me be precise about Velero, because this is the most-repeated wrong answer in this whole topic. Per the Velero docs, it does namespace- and label-scoped backups, CSI volume snapshots, scheduled backups, and restore into a new cluster. All of that assumes a source Kubernetes cluster. Coming from a PaaS there is no cluster, no cluster state, and nothing for Velero to back up. No source cluster, no Velero.

Argo CD and Flux are both CNCF Graduated (Flux graduated in November 2022, Argo in December 2022) and both do pull-based reconciliation. The difference is ergonomics.

Argo CDFlux
CNCF maturityGraduated (2022)Graduated (2022)
ReconciliationPull-based, app-of-appsPull-based, Kustomize/Helm controllers
UIRich built-in UI + SSOCLI and controller-first, no first-party UI
Multi-tenancy / RBACProjects, strong RBACNamespace and RBAC based
Helm & KustomizeBoth supportedBoth supported
Progressive deliveryArgo RolloutsFlagger
Best fitTeams who want a UI and SSO out of the boxTeams who want a lean, GitOps-native controller set

Pick Argo CD if you want a UI-first workflow with SSO and a visual sync status. Pick Flux if you want a CLI and controller-first setup with tight image automation.

What GitOps still leaves you to write yourself: Dockerfiles, Deployments, Services, Ingress, HPAs, per-environment overlays, secrets wiring, the CI build pipeline and the promotion workflow between environments. Argo Rollouts, External Secrets Operator and Kustomize are the pieces people forget to budget for.

Database migration is its own track entirely: pg_dump/pg_restore for small datasets, logical replication or AWS DMS for a low-downtime cutover, and a cutover plan you have rehearsed with a measured downtime window before you promise one.

What are the options for keeping deployment simple after the move to Kubernetes?

Four realistic patterns exist, and the right one depends on whether you have a platform team: raw GitOps (Argo CD or Flux + Helm) if you do, a Backstage or Humanitec portal you build and own if you have the headcount, a BYOC internal developer platform (Qovery, Porter, Northflank) if you want the PaaS workflow back on infrastructure you own, or a serverless container runtime (Cloud Run, App Runner, Azure Container Apps) if you are willing to give up Kubernetes entirely.

Be honest about which one fits you:

  • Argo CD/Flux + Helm is the better answer when you already have a platform team, a strict GitOps mandate, or heavy custom networking and service mesh requirements. If that is you, don't add an abstraction on top.
  • Backstage is a developer portal, not a deployment engine. It catalogs services and wires templates; it needs plugins and a team behind it to mean anything. Humanitec is a platform orchestrator you configure, not an out-of-the-box PaaS.
  • BYOC internal developer platforms (bring your own cloud / bring your own cluster) give developers the PaaS workflow while the cloud account stays yours. That matters for cost and compliance: the bill, Savings Plans, Reserved Instances and the data all stay in your own account.
  • Serverless container runtimes are the escape hatch if, after all this, you decide you don't actually need Kubernetes.

This is where Qovery fits, and it is the first time I'll mention it for a reason. Qovery installs into your own AWS, GCP, Azure or Scaleway account, or an existing Kubernetes cluster, and restores the workflow you are giving up: git-push deployments, preview/ephemeral environments per pull request, environment auto-stop for non-production, per-environment RBAC, managed cluster upgrades, and databases backed by managed cloud services. The cloud bill and any committed-use discounts stay in your name.

Tie it back to the Next.js example: a containerized Next.js app with a per-PR preview environment is the exact workflow teams miss most after leaving Vercel, and it is a layer-4 problem no provisioning or GitOps tool solves for you.

ToolLayer coveredRole in a managed-platform-to-Kubernetes migrationDeveloper self-service OOTB?Runs in your own cloud account?License
Terraform / OpenTofuProvisioningCreate cluster + infraNoYesOpen source
Crossplane / UpboundProvisioningInfra as Kubernetes APIsNoYesOpen source / commercial
VeleroStateCluster-to-cluster backup/restore onlyNoYesOpen source
Argo CDDeliveryReconcile manifests, UI-first GitOpsNoYesOpen source
FluxDeliveryReconcile manifests, CLI-first GitOpsNoYesOpen source
Helm / KustomizeDeliveryPackage and template manifestsNoYesOpen source
BackstageSelf-serviceDeveloper portal/catalog (build it yourself)PartialYesOpen source
HumanitecSelf-servicePlatform orchestrator you configurePartialYesCommercial
PorterSelf-serviceBYOC PaaS workflowYesYesCommercial
NorthflankSelf-serviceBYOC or managed PaaS workflowYesYes (BYOC)Commercial
QoverySelf-serviceBYOC IDP: restores push-to-deploy, previews, RBACYesYesCommercial

The self-service layer is not a nice-to-have. The 2024 DORA report found internal developer platforms correlate with higher individual productivity and team performance, with the caveat that a badly built one can slow throughput while it matures. In other words: the layer helps, and building it badly hurts. That is the argument for not hand-rolling it unless you have the team to do it well.

What does a realistic migration sequence and timeline look like?

Run it in five phases over weeks, not a weekend: parity inventory, cluster and networking, stateless services behind dual-run DNS, data cutover, decommission, with the rollback path defined before each phase starts. Small estates land in 4-8 weeks; anything with stateful services, compliance scope or dozens of apps runs a quarter or more.

  • Phase 0 - Parity inventory. List every add-on, environment variable, cron job, background worker, buildpack behavior and custom domain on the old platform. Turn it into a parity checklist you can test against. This is the phase people skip and regret.
  • Phase 1 - Cluster and networking. Cluster, VPC and subnets, Ingress, TLS, observability, secrets management (IRSA or EKS Pod Identity + External Secrets Operator), image registry.
  • Phase 2 - Stateless services. Containerize and deploy the stateless services, then run both platforms in parallel behind weighted DNS or a shared load balancer so you can shift traffic gradually.
  • Phase 3 - Data. Replicate Postgres and Redis to RDS/ElastiCache or an operator, rehearse the cutover at least twice, and measure the real downtime window before you promise one.
  • Phase 4 - Cutover and decommission. Shift traffic, keep the old platform warm for a defined rollback window, then decommission and re-check the bill 30 days later.

The traps that bite people, each worth watching for: NAT gateway and cross-AZ egress costs, forgotten cron jobs and one-off dynos, build-time vs runtime env vars (including NEXT_PUBLIC_ in Next.js), sticky sessions, image size inflating cold starts, and buildpack magic you now have to reproduce in a Dockerfile.

How much does running Kubernetes yourself actually cost versus the managed platform you are leaving?

At meaningful scale, compute on EKS, GKE or AKS is cheaper than per-dyno, per-instance or per-seat PaaS pricing, but the saving evaporates if you hire a dedicated platform team or let non-production clusters idle. Model the fully loaded cost, including engineering time and wasted capacity, before you commit.

The control-plane fees are small and nearly identical across clouds: EKS is $0.10 per cluster per hour, GKE charges $0.10 per cluster per hour, and AKS is $0.10 per hour on the Standard tier (free tier has no control-plane charge). Nodes and data transfer are where the real money goes.

The waste is the headline. The Cast AI Kubernetes Cost Benchmark found that, across thousands of clusters, organizations used only about 13% of provisioned CPU and 20% of memory on average. Most of what you pay for sits idle. And the classic post-migration surprise is the network bill: an AWS NAT Gateway is $0.045 per hour plus $0.045 per GB processed, with cross-AZ data transfer at $0.01 per GB in each direction. Chatty services across AZs quietly rack that up.

Cost driverManaged PaaSSelf-managed KubernetesKubernetes + IDP
ComputePer-dyno/instance/seat markupRaw EC2/GCE/VM, cheaper at scaleRaw cloud compute, same base
Control planeIncluded~$0.10/cluster/hr per cloud~$0.10/cluster/hr per cloud
DatabasesAdd-on, bundledRDS/Cloud SQL or operator, you run itManaged cloud DB, provisioned for you
Egress / NAT / CDNMostly includedNAT + cross-AZ + CDN, easy to overspendSame, but auto-stop trims non-prod
Engineering timeNear zeroHigh: a platform hire often exceeds the old billLower: self-service reduces tickets
Idle / non-prod wasteLow (you scale down easily)High (~80% of requested CPU unused)Lower with auto-stop + right-sizing
Discount ownershipVendor keeps itYou keep Savings Plans / CUDsYou keep Savings Plans / CUDs

Here is the line item nobody writes down: headcount. A fully loaded platform engineer often costs more per year than the entire PaaS invoice you are cancelling. If the migration exists purely to cut the bill, do that math first, because one hire can erase the whole saving.

The levers that actually move the number are well known: Karpenter or the cluster autoscaler, Spot capacity, right-sizing requests against real usage, auto-stopping non-production environments overnight and at weekends, and keeping Savings Plans and committed-use discounts in your own account. That last one is the core BYOC argument - the discounts and the data stay with you, not the platform vendor.

As an illustrative shape rather than a benchmark: if your PaaS bill is a few thousand dollars a month for a handful of services, a lift to EKS plus a platform hire can cost more in year one, and only pays off once you are running enough workloads that the per-unit saving outweighs the fixed engineering cost. Run your own numbers; don't trust a vendor's.

Frequently asked questions
What is the best tool to migrate from Heroku, Render or Vercel to Kubernetes on AWS?

There isn't one tool; there are four layers. Use Terraform or OpenTofu to provision EKS, pg_dump or AWS DMS to move your data, Argo CD or Flux to deliver manifests, and an internal developer platform (Qovery, Porter, Northflank, or a Backstage portal you build) to restore push-to-deploy. The layer teams underestimate is the last one, and it is what decides whether the migration sticks.

How do I migrate a Next.js app from Vercel to Kubernetes without losing ISR and preview deployments?

Build the app with output: 'standalone' in a multi-stage Dockerfile, then configure a custom cacheHandler backed by Redis or S3 so ISR stays consistent across pods (each pod has its own cache by default, per the Next.js self-hosting docs). Preview deployments become a per-PR namespace with its own DNS, TLS and seeded database, which you either automate yourself or get from an internal developer platform like Qovery. Watch NEXT_PUBLIC_ variables, since they are baked in at build time, not read at runtime.

Can Velero migrate applications from a PaaS like Heroku to Kubernetes?

No. Velero backs up and restores Kubernetes cluster resources and persistent volumes and can restore them into a different cluster, which makes it a cluster-to-cluster tool. Coming from Heroku, Render or Vercel there is no source cluster and no cluster state, so there is nothing for Velero to back up. Use pg_dump, logical replication or AWS DMS for the data instead.

Argo CD or Flux: which should I choose for a migration to EKS?

Both are CNCF Graduated and both do pull-based GitOps, so you won't go wrong either way. Choose Argo CD if you want a rich built-in UI with SSO and visual sync status. Choose Flux if you prefer a CLI and controller-first setup with strong image automation and a lean footprint.

Do I need Crossplane or Upbound, or is Terraform enough to provision EKS?

For most teams, Terraform or OpenTofu with the community EKS module is enough and is the faster path. Reach for Crossplane or Upbound only if you specifically want infrastructure reconciled by Kubernetes controllers and exposed to developers as custom APIs, and you have a platform team willing to run that extra control plane.

How long does it take to migrate from a managed platform to Kubernetes, and what does it cost?

A small, mostly stateless estate lands in roughly 4-8 weeks; anything with stateful services, compliance scope or dozens of apps runs a quarter or more. On cost, the control plane is about $0.10 per cluster per hour across EKS, GKE and AKS, but the real drivers are idle capacity (Cast AI measured ~13% CPU utilization on average), network egress, and the platform engineer you may need to hire, which often costs more than the PaaS bill you are deleting.

Romaric Philogene
About the author
Romaric Philogene

Romaric founded Qovery to make Kubernetes accessible to every engineering team. He writes about platform strategy, developer experience, and the future of cloud infrastructure.

Next step

Ship faster on infrastructure you control.

Qovery gives your team self-service deployments on your own AWS, GCP, Azure, or Scaleway account - or your existing Kubernetes cluster. Start deploying in under 10 minutes.