← Back to guides

How to migrate from EKS, GKE or AKS to a European managed Kubernetes provider

Max Heyer · Updated

Moving from EKS, GKE or AKS to a European managed Kubernetes provider is mostly not a Kubernetes problem. Deployments, Services and Helm charts run on any conformant cluster. The work sits in everything around the cluster: cloud IAM bound to service accounts, managed databases and queues, load balancer annotations, storage classes, object storage, monitoring and autoscaling. Plan for those, run both platforms in parallel, and switch DNS in a planned window with a written rollback. For a typical SaaS with 10 to 50 services, that takes four to seven weeks to cutover, plus a few weeks with the old platform on standby. Contracts, egress requests and customer notices can take longer than the engineering. AWS wants its egress request two months before the first transfer it credits, so file it before the assessment if you want the pilot and parallel run covered.

This guide is the sequence we use and recommend, whichever European provider you pick.

What actually changes when you leave a hyperscaler?

Upstream Kubernetes APIs stay the same. What changes is every place where your manifests or code talk to the cloud underneath. Run this against your current cluster to see where those places are. On GKE, also count every Ingress with no class at all, because the Google load balancer controller serves those by default.

# Load balancer and ingress settings that only one cloud understands
kubectl get svc,ingress -A -o yaml | grep -E "aws-load-balancer|alb.ingress.kubernetes.io|ingressClassName: (alb|gce)|ingress.class: \"?gce|cloud.google.com|networking.gke.io|azure-load-balancer|azure-dns-label-name|azure-pls|appgw.ingress|alb.networking.azure.io|webapprouting"

# Gateway API objects, if the CRDs are installed
kubectl get gatewayclass,gateway -A

# Storage classes and the volumes that use them
kubectl get sc
kubectl get pvc -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,SC:.spec.storageClassName,SIZE:.spec.resources.requests.storage

# Service accounts bound to cloud identities
kubectl get sa -A -o yaml | grep -E "eks.amazonaws.com/role-arn|iam.gke.io/gcp-service-account|azure.workload.identity"
# EKS Pod Identity and GKE direct IAM bindings leave no annotation:
aws eks list-pod-identity-associations --cluster-name <cluster>
gcloud asset search-all-iam-policies --query="policy:svc.id.goog"

# Images pulled from a cloud registry
kubectl get pods -A -o jsonpath='{..image}' | tr ' ' '\n' | grep -E 'ecr|pkg.dev|gcr.io|azurecr' | sort -u

Then list the managed services your applications call: databases, caches, queues, secrets stores, container registries, DNS zones, buckets, monitoring and logging. Each one becomes a line in the mapping below.

Check two more things. First, the Kubernetes version. EKS keeps older versions running for longer through paid extended support, while most targets support only the three newest minor releases. If your source is behind, scan for removed APIs with a tool such as pluto or kubent and update manifests before you move. Second, the CPU architecture. If you run on Graviton or other ARM nodes, build multi-arch images or confirm that the target offers ARM nodes.

How do you map cloud-specific dependencies?

This table is the core of the migration plan. The left columns are what EKS, GKE and AKS document today. The right column is the portable replacement that works on any upstream cluster, including European ones.

DependencyAWS (EKS)Google Cloud (GKE)Azure (AKS)Portable replacement
Pod identity to cloud APIsEKS Pod Identity or IAM roles for service accounts (IRSA)Workload Identity Federation for GKEMicrosoft Entra Workload IDScoped credentials in Kubernetes Secrets, synced by External Secrets Operator; OIDC federation where the target supports it
Load balancer behaviourservice.beta.kubernetes.io/aws-load-balancer-type and related annotations; AWS Load Balancer Controller Ingressspec.loadBalancerClass: networking.gke.io/l4-regional-internal or the older networking.gke.io/load-balancer-type: "Internal" annotation; GKE Ingress and Gateway classesservice.beta.kubernetes.io/azure-load-balancer-internal: "true"; Application Gateway ingresstype: LoadBalancer for L4, plus an ingress controller or Gateway API implementation in the cluster for L7
Block storageEBS CSI driverstandard-rwo (balanced PD), premium-rwo (SSD PD)managed-csi, managed-csi-premiumThe target’s storage class; remap names on restore
Node autoscalingKarpenter or Cluster AutoscalerCluster autoscaler, AutopilotCluster autoscalerCluster Autoscaler where the target supports it; otherwise fixed node pools sized for peak
TLS certificatesAWS Certificate Manager on the load balancerGoogle-managed certificatesKey Vault certificates on Application Gatewaycert-manager with ACME
ObservabilityCloudWatch, Container InsightsCloud Logging, Cloud MonitoringAzure Monitor, Container InsightsPrometheus, Grafana and Loki, or another OpenTelemetry backend
Managed Postgres / MySQLRDS, AuroraCloud SQL, AlloyDBAzure Database for PostgreSQL / MySQLThe target’s managed database, or an operator such as CloudNativePG on the cluster
NoSQLDynamoDBFirestore, BigtableCosmos DBNo drop-in replacement; plan a data model change
Queues and streamsSQS, SNS, KinesisPub/SubService Bus, Event HubsNATS, RabbitMQ or Kafka (Strimzi) on the cluster
Object storageS3Cloud StorageBlob StorageAny S3-compatible service. S3 clients change endpoint, keys and sometimes checksum settings; Cloud Storage and Blob SDK code moves to an S3 client
SecretsSecrets ManagerSecret ManagerKey VaultExternal Secrets Operator with Vault or OpenBao, or the target’s secrets store
Container registryECRArtifact RegistryACRHarbor, or the target’s registry

Checked October 2026.

The table leaves out services that rarely have a portable equivalent: CDN and WAF, functions, email sending, and in-cluster operators that create cloud resources (AWS Controllers for Kubernetes, Config Connector, Azure Service Operator). List them anyway, because each one needs a decision.

If you run the community ingress-nginx controller, do not carry it over. Kubernetes retired the project and archived it in March 2026, so it gets no more security fixes. Use the move to switch to a Gateway API implementation or another maintained controller.

Two rows deserve extra time.

Identity. Workload identity on a hyperscaler means pods never see a long-lived credential. Check whether your target has an equivalent that federates into its own APIs. If not, decide early whether scoped static keys stored as Secrets and rotated on a schedule are acceptable.

Databases. The database is usually the longest part of the migration and the part that sets the length of the write freeze. For Postgres, logical replication from RDS or Cloud SQL to the target lets you keep the freeze to minutes. It has limits: it does not copy schema changes or sequence values, so freeze DDL and reset sequences on the target before you switch. Tables need a primary key or replica identity for updates and deletes to replicate. On RDS you enable it with the rds.logical_replication parameter, which needs a reboot. The source must also be reachable from the target, usually over public access with TLS or a VPN you run yourself. For smaller databases, pg_dump and pg_restore inside a maintenance window is simpler and easier to reason about.

How do you move S3 data and volumes?

Object storage is the easiest data to move because S3 is a de facto standard. rclone talks to S3, Google Cloud Storage and Azure Blob, so one tool covers all three sources:

# Dry run first: sync deletes objects on the target that are not in the source
rclone sync aws:my-bucket target:my-bucket --checksum --dry-run

# First pass: copy everything while production keeps writing
rclone sync aws:my-bucket target:my-bucket --checksum --transfers 32 --progress

# Repeat until the delta is small, then once more after the write freeze
rclone sync aws:my-bucket target:my-bucket --checksum

# Verify before you switch the endpoint. Objects uploaded in parts have no MD5,
# so rclone compares them by size only; add --download to compare content
# (this reads every object again and costs egress).
rclone check aws:my-bucket target:my-bucket

# Buckets with Object Lock: copy each object's retention and legal hold (rclone v1.74 or later)
rclone sync aws:my-bucket target:my-bucket --checksum --metadata \
  --s3-object-lock-mode copy --s3-object-lock-retain-until-date copy \
  --s3-object-lock-legal-hold-status copy

Sync is incremental, so you can run it daily during the parallel run and do a final pass at cutover. rclone copies only current object versions unless you add --s3-versions. rclone does not copy per-object Object Lock retention or legal holds by default. From v1.74 it can, if you add --metadata and set the --s3-object-lock-* flags to copy as in the last example. Copy bucket policies, versioning, lifecycle rules and default Object Lock retention separately, because rclone does not move bucket configuration. If you rely on Object Lock, create the target bucket with it enabled, because some S3-compatible services, enum included, only allow it at bucket creation.

For PersistentVolumes, Velero with file-system backup copies volume data between clusters. Its restore can rewrite storage class names through a change-storage-class-config ConfigMap, so gp3 or standard-rwo claims land on the target’s class without editing manifests. File-system backup does not capture a volume at one point in time, so move databases with dumps or replication, not Velero, and stop writes to a volume before its final copy.

Egress fees. Moving data out of a hyperscaler costs egress, but all three now have a process for customers who leave:

  • AWS credits data transfer out to the internet for customers moving off AWS, after a request to AWS Support. You must move all data off the eligible accounts or off a particular service. You don’t have to close the account, but by the end of the transition period you must delete all remaining data and workloads from the services you leave. AWS asks for the request at least two months before your planned switch initiation date and says not to start the move before it approves, so data you move earlier, for example during a pilot, is billed at normal rates. Customers outside the EU have 180 days. EU customers follow the AWS EU Data Act Addendum: a 30-day transitional period, which AWS can extend up to seven months when the move is technically unfeasible. UK customers follow a separate UK switching addendum. Transfers through CloudFront, Direct Connect, Snow and Global Accelerator are not credited.
  • Google Cloud credits data transfer for customers who migrate all data off a service. You file an Exit Notice. Within 14 days Google assigns a billing support agent, then you have a 30-day Initiation Period and at least 30 days to migrate. The credit covers data in listed storage and database services such as Cloud Storage, Persistent Disk and Cloud SQL. You file a Completion Notice when you are done, and the agreement for that service ends.
  • Azure credits internet data transfer out for customers who leave, beyond the 100 GB per month that is free for everyone. You log a request with Azure Support, then have 60 days from your stated transfer start date to move the data, and you must cancel all Azure subscriptions on the account before Microsoft applies the invoice-level credit. ExpressRoute, VPN, Front Door and CDN traffic is not credited. Customers with a UK billing address moving data out of UK datacenters get 180 days and can leave single services.

Read the current terms before you start, because every program needs a request in advance and none applies retroactively.

For customers in the EU, the EU Data Act has limited switching charges, egress included, to cost since 12 September 2025 and bans them from 12 January 2027. Check commitments such as Savings Plans, reservations or committed-spend contracts before you set a date.

Exit credits do not cover a parallel run. Microsoft documents at-cost data transfer for customers with an EEA, EFTA or UK billing address who run Azure in parallel with another provider, requested per destination network. Google’s Data Transfer Essentials makes such traffic from European regions free for now, but only to recognized destination networks; for others, ask Google support. Check that your target’s network qualifies before you plan daily syncs.

How do you run both platforms in parallel?

Stand up the target cluster next to the old one and deploy everything through the same GitOps pipeline (Argo CD or Flux) with a per-cluster overlay for the parts that differ: storage classes, annotations, secrets and endpoints. If your manifests deploy cleanly to both clusters from one repository, you have found all the cloud-specific parts.

Move one non-critical service first. It teaches your team the new platform before production depends on it, and it tests your observability: logs, metrics and alerts must work on the target before any real traffic arrives.

During the parallel run, keep data flowing in one direction: old platform is the source of truth, target is a replica. Database replication, daily rclone syncs and Velero restores keep the target close enough that cutover is a short final sync.

How do you cut over DNS without downtime?

  1. Two days before: lower the TTL on every record you will move to 60 seconds. Resolvers need the old TTL to expire before the short one takes effect.
  2. Before the window: import your zone at the new DNS provider and check every record against the old one. Issue TLS certificates on the target in advance. cert-manager with DNS-01 validation works without traffic reaching the new cluster, as long as the solver writes to the DNS provider that serves the zone today, or you CNAME _acme-challenge to a zone you control.
  3. Before the window: give every partner that allowlists your IP addresses the target’s outbound IPs, and confirm that the target can give you a stable egress IP. Check whether client IPs reach your apps through the new load balancer. If not, fix rate limiting and audit logging that depend on them.
  4. In the window: freeze writes, run the final database and object sync, verify row counts and rclone check, then point the records at the target load balancer.
  5. After the switch: watch error rates and latency for at least an hour, then raise TTLs again once you are confident.

If you also move the DNS zone itself, change nameservers at the registrar as a separate step on a different day. Two changes in one window make a rollback harder to reason about.

What does a rollback plan look like?

Write it before the cutover and agree on the triggers: for example, error rate above a fixed threshold for 10 minutes, or a failed data check. The plan itself is short:

  • Keep the old cluster, databases and buckets running and untouched until you sign off.
  • Rolling back is pointing DNS at the old load balancers again. With 60-second TTLs, most new connections follow within minutes. Long-lived connections and clients that cache DNS follow only when they reconnect, so keep both sides serving until they drain.
  • Decide in advance how writes made on the target after cutover get back to the old platform: reverse replication, a replay from logs, or accepting a known gap. This is the step teams skip.
  • Delete nothing on the old platform until the target has run a full business cycle, including at least one month-end, often two to four weeks.

How long does a migration take?

Start the slow clocks before the assessment, because they run on their own schedule:

  • Egress credits. AWS asks for the request at least two months before the first bulk transfer you want credited. Filed in week one, it covers only data moved after the two months and after approval, which on this schedule excludes the parallel run and the cutover.
  • Sub-processor notices. If you process personal data for your customers, check what your data processing agreements (GDPR Art. 28(2)) say about adding a new provider. Many DPAs set a notice period of 30 days.
  • Financial-sector customers. DORA Art. 28(3) covers informing the supervisor about planned arrangements for critical or important functions.

A typical engineering schedule for a SaaS with 10 to 50 services, one or two Postgres databases and a few terabytes of object storage:

PhaseWhat happensTypical duration
AssessmentInventory, dependency mapping, target architecture, date1 week
PilotTarget cluster up, one non-critical service live1 to 2 weeks
Parallel runAll services deployed to both, data syncing, load tests2 to 4 weeks
CutoverWrite freeze, final sync, DNS switch1 planned window
DecommissionOld platform kept for rollback, then shut down2 to 4 weeks

S3-only migrations of up to a few tens of terabytes can finish in days. Large databases, compliance sign-offs and change-freeze calendars push this out more than Kubernetes itself does.

How do you choose the European target?

OVHcloud, IONOS, Scaleway, STACKIT, Exoscale and enum all sell managed Kubernetes. All of them except enum run more than one region and have a longer track record; enum runs one production region in Frankfurt, and a Berlin region can be set up on request. Compare on the things that decide your migration:

  • Jurisdiction. Is the provider’s parent company European? An EU region does not change who owns the provider.
  • Upstream Kubernetes and version window. Can you run the same manifests and Helm charts without forks? Is the offer listed as CNCF Certified Kubernetes?
  • Zones. Can the control plane and node pools span several availability zones, as they usually do on a hyperscaler?
  • Scaling. Is there a cluster autoscaler, or do you size node pools for peak?
  • Load balancing. L4 only or L7 too, and does the client IP reach your pods?
  • Storage. S3 compatibility details you depend on (versioning, lifecycle, Object Lock), block volumes, and volume snapshots if your backups use them.
  • Managed services you will not run yourself. If you need a managed database or queue, check that the provider has one; otherwise budget for running it on the cluster.
  • Identity. How pods authenticate to the provider’s own APIs.
  • SLA and support. What is covered, which credits apply, and who answers when it breaks, at what hours.
  • Certifications. Ask for the provider’s own certificates, not the data center’s.
  • Exit. The egress price and the provider’s own switching terms.

This guide is general information, not legal advice. Have your counsel assess your specific situation.

Where enum fits

enum Kubernetes Engine is upstream Kubernetes with its own highly available control plane per cluster, run in Frankfurt by enum GmbH, a German company with no US parent and no US company in its structure. Object storage is S3-compatible at fra.storage.enum.cloud, block storage is available as Kubernetes PersistentVolumes, and DNS is free with BIND import and a cert-manager webhook for DNS-01.

The limits that matter for a migration:

  • One region, one zone. Clusters run in zone fra-a. The control plane is replicated across three failure domains in that site, not across availability zones.
  • No autoscaling. There is no cluster or node autoscaler, so you size node pools for peak. Node types are general purpose x86 (AMD EPYC) only, with no ARM.
  • Networking. Load balancers are L4 (TCP and UDP), so you run your own ingress or Gateway controller. Client IP preservation is not available yet. Outbound NAT is included, but a stable egress IP is still to come. There is no VPN gateway or private interconnect, so database replication from a hyperscaler runs over the internet with TLS. enum’s network (AS215998) is not on Google’s Data Transfer Essentials list today. There is no CDN, WAF or DDoS protection product yet, so keep those with another provider.
  • Managed services. enum has no managed database, queue, container registry or secrets manager, so those run on the cluster or with another provider. There is no workload identity: pods reach object storage with scoped access keys stored as Secrets. There are no customer-managed keys (KMS) yet.
  • Storage quotas. Object storage has a soft limit of 10 TiB and 100 buckets per project. Ask for a higher quota before the first sync if you are moving more.
  • Volumes. enum does not document volume snapshots, so Velero file-system backup is the path for volume data.
  • Tooling. Clusters are set up on request today; self-service cluster creation, a Terraform provider and ExternalDNS support are planned for Q4 2026. VPC is in preview, and roles and fine-grained IAM permissions are planned for Q4 2026.
  • SLA and support. enum states 99.9% for the control plane API. Worker nodes have no SLA, and credit terms are not published; ask for the contract terms. Included support covers working days; 24/7 support for critical incidents is part of Enterprise Support.
  • Compliance. enum is not listed as CNCF Certified Kubernetes. ISO 27001 certification is in progress (target Q4 2026) and BSI C5 is on the roadmap for 2027. enum has not published a sub-processor list, so ask for one before you send your customers a sub-processor notice.

The migration page describes how enum’s engineers plan and run a move with your team, and enum Kubernetes Engine has the product details.

FAQ

Do I need to change application code to leave EKS, GKE or AKS? Usually not for the Kubernetes part. Code changes come from cloud SDK calls: S3 clients need a new endpoint and credentials, code on the Cloud Storage or Azure Blob SDKs moves to an S3 client, and code that calls SQS, Pub/Sub, DynamoDB or a cloud secrets API needs a replacement.

Can I migrate without downtime? Stateless services, yes, with a parallel run and a DNS switch. Databases usually need a short write freeze; logical replication keeps it to minutes.

Is moving data out of AWS, Google Cloud or Azure free? Often. AWS, Google Cloud and Azure credit egress for customers who leave, after a request made in advance and under conditions. For customers in the EU, the EU Data Act also limits switching charges, egress included, to cost today and bans them from 12 January 2027. Exit credits do not cover egress during a parallel run.

Which tool should I use to copy S3 buckets? rclone for objects, because it reads S3, Google Cloud Storage and Azure Blob and syncs incrementally. Run the first sync with --dry-run, and copy bucket policies, lifecycle rules and default Object Lock retention separately.

How long does a Kubernetes migration take? For 10 to 50 services, plan on four to seven weeks of engineering from assessment to cutover, plus two to four weeks with the old platform on standby for rollback. File the AWS egress request at least two months before the first bulk transfer you want credited, which usually means before the assessment. Start customer notices at the same time.

Build on enum.
Start today.

Sign up and use object storage and DNS right away, or talk to us about Kubernetes.