Cloud Migration - Breaking Free from Hyperscaler Lock-in

Vendor lock-in at cloud hyperscalers, the portability obligations of the EU Data Act and a five-phase migration path with Polycrate to sovereign Kubernetes-native infrastructure, with examples such as AWS RDS to CloudNativePG.

Cloud Migration - Breaking Free from Hyperscaler Lock-in

The challenge: vendor lock-in at cloud hyperscalers

Cloud hyperscalers such as AWS, Azure and Google Cloud offer convenient, managed services – but at a high price: proprietary APIs, non-standardized data formats, egress fees and contractual hurdles create systematic vendor lock-in.

Typical lock-in mechanisms:

  • 🔒 Proprietary services: RDS, DynamoDB, S3, Lambda with AWS-specific APIs
  • 💰 Egress fees: high costs for data transfer when switching
  • 📦 Non-portable formats: vendor-specific IaC (CloudFormation, ARM templates)
  • 🔗 Service chaining: deeply integrated services make partial migration difficult
  • 📄 Contractual binding: enterprise agreements with discount structures bind long-term

The consequences:

  • ❌ No ability to switch in case of price increases or quality problems
  • ❌ Dependence on third-country jurisdiction (CLOUD Act, FISA)
  • ❌ Lack of sovereignty over your own infrastructure
  • ❌ Difficult compliance (DORA, NIS-2, Data Act)

The EU Data Act: portability as an obligation

The Data Act (EU 2023/2854) enters into force on September 12, 2025 and creates binding rules for cloud switching and interoperability.

Core Data Act requirements for cloud providers

Requirement Description Deadline
Open interfaces Standardized, documented APIs for export Immediately
Machine-readable formats Data in structured, common formats Immediately
Functional equivalence At least for IaaS features Immediately
Egress fee phase-out Gradual reduction down to €0 By 12.09.2028
Multi-cloud support Enable parallel operation and gradual migration Immediately
Documented switching processes Transparent switching processes before contract Immediately

What does this mean for companies?

  • ✅ Right to switch – cloud customers must be able to switch
  • ✅ Portability – full export of data, apps, configurations
  • ✅ Interoperability – open standards instead of proprietary formats
  • ✅ Free of charge – no egress fees from 2028
  • ✅ Independence – no contractual or technical lock-ins

But: the Data Act demands rights – it does not provide the tools to implement them.

Polycrate: the ideal tool for sovereign cloud migration

Polycrate was built to enable standardized, portable infrastructure automation – exactly what the Data Act demands and what hyperscalers have prevented for years.

Why Polycrate for cloud migration?

1. Ansible: industry standard for heterogeneous infrastructure

Polycrate uses Ansible as its core engine – the de-facto industry standard for automating heterogeneous IT landscapes.

graph LR
    Polycrate[Polycrate] --> Ansible[Ansible]
    Ansible --> AWS[AWS]
    Ansible --> Azure[Azure]
    Ansible --> GCP[Google Cloud]
    Ansible --> K8s[Kubernetes]
    Ansible --> Linux[Linux Hosts]
    Ansible --> Docker[Docker]

    style Ansible fill:#e74c3c,color:#fff
    style Polycrate fill:#3498db,color:#fff

Advantages:

  • ✅ Non-proprietary: Ansible is open source, no vendor lock-in
  • ✅ Universally applicable: AWS, Azure, GCP, bare metal, K8s, Docker
  • ✅ Declarative: infrastructure as code, versionable, reproducible
  • ✅ Established: over 60% of companies use Ansible (Red Hat Survey 2023)
  • ✅ Agentless: no software installation on target systems required

2. Kubernetes-native architecture

Polycrate focuses on Kubernetes as the target platform – the only truly cloud-agnostic infrastructure layer.

# workspace.poly - Kubernetes-native app deployment
name: my-workspace

blocks:
  - name: my-cluster
    from: registry.my-org.com/blocks/k8s/cluster:1.0.0

  - name: my-app
    from: registry.my-org.com/blocks/k8s/my-app:1.0.0
    kubeconfig:
      from: my-cluster  # Uses the kubeconfig from the cluster block
    config:
      namespace: production
      replicas: 3
      persistence:
        enabled: true
        size: 50Gi

Why Kubernetes?

  • 🌍 Cloud-agnostic: runs identically on AWS, Azure, GCP, bare metal
  • 📦 Container-native: standard artifacts (OCI images), no proprietary formats
  • 🔄 Portable: workloads can be moved between clusters
  • 🛠️ Standardized: CNCF standards (kubectl, Helm, operators)
  • 🔓 Lock-in-free: no vendor can "own" Kubernetes

3. Free, production-ready blocks from the PolyHub

The PolyHub offers curated, free blocks for common infrastructure patterns – as hyperscaler alternatives.

Examples: hyperscaler → K8s-native alternative

Hyperscaler service Polycrate block (open source) Technology
AWS RDS (PostgreSQL) ayedo/k8s/cloudnative-pg CloudNativePG Operator
AWS ElastiCache (Redis) ayedo/k8s/redis Redis with Sentinel HA
AWS DocumentDB (MongoDB) ayedo/k8s/mongodb MongoDB with replica sets
AWS ALB/ELB ayedo/k8s/nginx NGINX Ingress Controller
AWS Certificate Manager ayedo/k8s/cert-manager cert-manager + Let's Encrypt
AWS ECR ayedo/k8s/harbor Harbor container registry
AWS CloudWatch ayedo/k8s/victoria-metrics-stack VictoriaMetrics + Grafana
AWS Secrets Manager ayedo/k8s/hashicorp-vault HashiCorp Vault
AWS SQS/SNS ayedo/k8s/rabbitmq-operator RabbitMQ Operator

All PolyHub blocks are:

  • 🆓 Open source (Apache 2.0)
  • ✅ Production-tested
  • 📖 Fully documented
  • 🔄 Versioned (semantic versioning)
  • 🧪 Extensible (via inheritance)
# Pull a block from the PolyHub
polycrate blocks pull cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1

# Deploy
polycrate run ayedo/k8s/cloudnative-pg install

4. Extensive cloud-native toolchain preinstalled

The Polycrate container ships all the tools needed for cloud migration – no local installations required.

Kubernetes & containers:

kubectl v1.32.1     - Kubernetes CLI
helm v3.18.4        - Package manager for Kubernetes
cilium v0.18.5      - Networking & security
velero v1.16.1      - Backup & disaster recovery
argocd v3.0.6       - GitOps deployment
docker              - Container runtime CLI
skopeo              - Container image management

Automation & IaC:

ansible-core        - Automation engine
yq v4.45.1          - YAML/JSON processor
jq                  - JSON processor
age v1.2.1          - Encryption tool

Cloud provider CLIs (via Ansible):

AWS (boto3)         - AWS SDK for Python
Hetzner (hcloud)    - Hetzner Cloud API client
VMware (PyVmomi)    - VMware vSphere API
OpenStack           - OpenStack APIs
Azure               - Azure SDKs (via Ansible modules)
GCP                 - Google Cloud SDKs (via Ansible modules)

Migration-specific tools:

rsync               - Data synchronization
openssh-client      - Remote access
docker-compose      - Multi-container apps
git                 - Version control
apache2-utils       - HTTP benchmarking (ab, htpasswd)

Ansible collections for providers:

community.general   - 1000+ modules for various systems
community.docker    - Docker & Docker Compose automation
community.vmware    - VMware vSphere automation
netbox.netbox       - IPAM & DCIM integration

Assessment workflow with Polycrate:

# workspace.poly - migration assessment
name: aws-migration-assessment

blocks:
  - name: aws-inventory
    actions:
      - name: scan-resources
        playbook: scan-aws-resources.yml

      - name: export-rds-schemas
        playbook: export-rds-schemas.yml

      - name: analyze-dependencies
        playbook: analyze-dependencies.yml

  - name: cost-analysis
    actions:
      - name: calculate-savings
        playbook: calculate-savings.yml

Example: RDS database assessment with preinstalled tools

# scan-aws-resources.yml
---
- name: Scan AWS RDS Instances
  hosts: localhost
  tasks:
    - name: Get all RDS instances
      amazon.aws.rds_instance_info:
        region: eu-central-1
      register: rds_instances

    - name: Export to JSON
      copy:
        content: "{{ rds_instances | to_nice_json }}"
        dest: "{{ artifacts_root }}/aws-rds-inventory.json"

    - name: Analyze schemas
      shell: |
        for instance in $(cat {{ artifacts_root }}/aws-rds-inventory.json | jq -r '.instances[].db_instance_identifier'); do
          pg_dump -h ${instance}.abc123.eu-central-1.rds.amazonaws.com \
            -U admin \
            --schema-only \
            -f {{ artifacts_root }}/${instance}-schema.sql
        done

Execution:

# Everything runs in the Polycrate container - no local tools required!
polycrate run aws-inventory scan-resources
polycrate run aws-inventory export-rds-schemas
polycrate run aws-inventory analyze-dependencies
polycrate run cost-analysis calculate-savings

Advantages:

  • ✅ No local installation: all tools in the container, consistent versions
  • ✅ Cross-provider: AWS, Azure, GCP, VMware, Hetzner – all included
  • ✅ Assessment-ready: start with inventory right away
  • ✅ Migration-ready: schema export, data sync, testing – all available
  • ✅ Versioned: exactly defined tool versions (see Dockerfile.goreleaser)

5. Provider-independent infrastructure as code

Polycrate workspaces are fully portable – no lock-in to tools, providers or platforms.

# workspace.poly - provider-independent description
name: production-stack

blocks:
  # Cluster on any provider (Linux servers)
  - name: k8s-cluster
    from: cargo.ayedo.cloud/ayedo/linux/k8s-1.27:0.3.7
    config:
      servers: 3
      agents: 5

  # Apps on the cluster (provider-agnostic)
  - name: postgres
    from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1
    kubeconfig:
      from: k8s-cluster
    config:
      namespace: databases
      persistence:
        size: 100Gi
        storageClass: fast-ssd

  - name: app
    from: webapp-base:1.0.0
    kubeconfig:
      from: k8s-cluster
    config:
      domain: myapp.example.com
      replicas: 3

workflows:
  - name: deploy-complete-stack
    steps:
      - name: provision-cluster
        block: k8s-cluster
        action: provision
      - name: deploy-postgres
        block: postgres
        action: install
      - name: deploy-app
        block: app
        action: install

This workspace runs identically on:

  • ☁️ AWS (via Hetzner Cloud, EKS, EC2)
  • ☁️ Azure (AKS, VMs)
  • ☁️ Google Cloud (GKE, Compute Engine)
  • 🖥️ Bare metal (your own servers)
  • 🏠 On-premise (VMware, Proxmox)

Switching? → Just swap the kubeconfig, everything else stays identical.

6. OCI-registry-based sharing

Polycrate uses OCI-compatible registries (Docker, Harbor, etc.) for block distribution – an open standard, no proprietary format.

# Develop a block
cd blocks/my-custom-postgres

# Push to a private registry
polycrate block push registry.company.com/infra/postgres:1.0.0

# Use in another workspace
polycrate blocks pull registry.company.com/infra/postgres:1.0.0

Data Act compliant:

  • ✅ Machine-readable format (OCI Image Spec)
  • ✅ Open standard (no vendor lock-in)
  • ✅ Portable (any OCI registry works)
  • ✅ Versioned (image tags)

The migration path: from hyperscaler to sovereign K8s-native infrastructure

Phase 1: assessment & planning

Goal: inventory of the current hyperscaler dependencies

# Workspace for migration assessment
name: migration-assessment

blocks:
  - name: inventory
    actions:
      - name: scan-aws
        playbook: scan-aws.yml

      - name: analyze-dependencies
        playbook: analyze-dependencies.yml

Result: inventory of all resources, service dependencies, migration candidates

Phase 2: target architecture design

Goal: identify K8s-native alternatives for every hyperscaler service

Migration strategy:

graph TB
    AWS[AWS Services] --> Analysis[Analysis & Mapping]
    Analysis --> K8s[Kubernetes-Native<br/>Alternatives]

    AWS --> RDS[RDS PostgreSQL]
    AWS --> ElastiCache[ElastiCache Redis]
    AWS --> ELB[ALB/ELB]
    AWS --> ECR[ECR]
    AWS --> CloudWatch[CloudWatch]

    RDS --> PG[CloudNativePG<br/>ayedo/k8s/cloudnative-pg]
    ElastiCache --> Redis[Redis Sentinel<br/>ayedo/k8s/redis]
    ELB --> Ingress[NGINX Ingress<br/>ayedo/k8s/nginx]
    ECR --> Harbor[Harbor Registry<br/>ayedo/k8s/harbor]
    CloudWatch --> Prom[VictoriaMetrics + Grafana<br/>ayedo/k8s/victoria-metrics-stack]

    K8s --> PG
    K8s --> Redis
    K8s --> Ingress
    K8s --> Harbor
    K8s --> Prom

    style AWS fill:#ff9900,color:#fff
    style K8s fill:#326ce5,color:#fff
    style Analysis fill:#3498db,color:#fff

Phase 3: parallel build-up with Polycrate

Goal: build the new K8s-native infrastructure in parallel to the existing one

# workspace.poly - migration workspace
name: migration-production

blocks:
  # New K8s cluster on Linux servers
  - name: new-cluster
    from: cargo.ayedo.cloud/ayedo/linux/k8s-1.27:0.3.7
    config:
      servers: 3
      agents: 5

  # PostgreSQL migration (RDS → CloudNativePG)
  - name: postgres-migration
    from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1
    kubeconfig:
      from: new-cluster
    config:
      namespace: databases
      app:
        persistence:
          size: 200Gi  # Take the size from RDS
          storageClass: fast-ssd
        backup:
          enabled: true
          schedule: "0 2 * * *"

    actions:
      - name: migrate-from-rds
        playbook: migrate-rds.yml

  # Redis migration (ElastiCache → Redis Sentinel)
  - name: redis-migration
    from: cargo.ayedo.cloud/ayedo/k8s/redis:0.1.5
    kubeconfig:
      from: new-cluster
    config:
      namespace: cache
      persistence:
        enabled: true
        size: 50Gi

  # Ingress (ALB → NGINX)
  - name: ingress
    from: registry.my-org.com/blocks/k8s/nginx:1.0.0
    kubeconfig:
      from: new-cluster

  # Monitoring (CloudWatch → VictoriaMetrics + Grafana)
  - name: monitoring
    from: cargo.ayedo.cloud/ayedo/k8s/victoria-metrics-stack:1.0.0
    kubeconfig:
      from: new-cluster
    config:
      retention: 30d
      storage: 100Gi

workflows:
  - name: setup-target-infrastructure
    steps:
      - name: provision-cluster
        block: new-cluster
        action: provision

      - name: deploy-ingress
        block: ingress
        action: install

      - name: deploy-monitoring
        block: monitoring
        action: install

      - name: deploy-postgres
        block: postgres-migration
        action: install

      - name: deploy-redis
        block: redis-migration
        action: install

  - name: migrate-data
    prompt:
      message: "Start data migration? (Downtime required)"
    steps:
      - name: backup-rds
        block: postgres-migration
        action: backup-from-rds

      - name: stop-old-app
        block: legacy-app
        action: stop

      - name: restore-to-cloudnativepg
        block: postgres-migration
        action: restore

      - name: verify-data
        block: postgres-migration
        action: verify

      - name: start-new-app
        block: new-app
        action: start

Execution:

# Build the target infrastructure
polycrate workflows run setup-target-infrastructure --blocks-auto-pull

# Perform the data migration
polycrate workflows run migrate-data --force

# Verification
polycrate run postgres-migration verify
polycrate run new-app health-check

Phase 4: cutover & validation

Goal: redirect traffic to the new infrastructure, shut down the old one

# Cutover workflow
workflows:
  - name: cutover
    prompt:
      message: "Start production cutover?"
    steps:
      # Switch DNS/load balancer to the new system
      - name: update-dns
        block: dns
        action: point-to-new-cluster

      # Traffic validation
      - name: monitor-traffic
        block: monitoring
        action: watch-metrics

      # Stop old services (after soak period)
      - name: shutdown-old-services
        block: legacy
        action: stop

Phase 5: decommission hyperscaler resources

Goal: tear down the old hyperscaler resources

workflows:
  - name: cleanup-aws
    prompt:
      message: "Really delete AWS resources?"
    steps:
      - name: delete-rds
        block: aws-cleanup
        action: delete-rds-instance

      - name: delete-elasticache
        block: aws-cleanup
        action: delete-elasticache-cluster

      - name: delete-alb
        block: aws-cleanup
        action: delete-load-balancers

Practical examples

Example 1: PostgreSQL migration (AWS RDS → CloudNativePG)

# workspace.poly - PostgreSQL migration block definition
name: migration-production

blocks:
  - name: postgres-migration
    from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1
    kubeconfig:
      from: new-cluster
    config:
      namespace: databases
      aws:
        rds_endpoint: "mydb.abc123.eu-central-1.rds.amazonaws.com"
        rds_database: "production"
        rds_user: "admin"
    actions:
      - name: backup-from-rds
        playbook: backup-rds.yml
      - name: restore
        playbook: restore.yml
      - name: verify
        playbook: verify.yml
# backup-rds.yml
---
- name: Backup from AWS RDS
  hosts: localhost
  tasks:
    - name: Create backup from RDS
      ansible.builtin.shell: |
        pg_dump \
          -h {{ block.config.aws.rds_endpoint }} \
          -U {{ block.config.aws.rds_user }} \
          -d {{ block.config.aws.rds_database }} \
          -F custom \
          -f {{ artifacts_root }}/blocks/postgres-migration/rds-backup.dump
      environment:
        PGPASSWORD: "{{ lookup('env', 'RDS_PASSWORD') }}"

    - name: Verify backup
      ansible.builtin.stat:
        path: "{{ artifacts_root }}/blocks/postgres-migration/rds-backup.dump"
      register: backup_file

    - name: Fail if backup missing
      ansible.builtin.fail:
        msg: "Backup file not created"
      when: not backup_file.stat.exists
# restore.yml
---
- name: Restore to CloudNativePG
  hosts: localhost
  tasks:
    - name: Get CloudNativePG primary pod
      kubernetes.core.k8s_info:
        kubeconfig: "{{ kubeconfig_path }}"
        kind: Pod
        namespace: "{{ block.config.namespace }}"
        label_selectors:
          - "cnpg.io/cluster={{ block.name }}"
          - "role=primary"
      register: primary_pod

    - name: Copy backup to pod
      kubernetes.core.k8s_cp:
        kubeconfig: "{{ kubeconfig_path }}"
        namespace: "{{ block.config.namespace }}"
        pod: "{{ primary_pod.resources[0].metadata.name }}"
        remote_path: "/tmp/backup.dump"
        local_path: "{{ artifacts_root }}/blocks/postgres-migration/rds-backup.dump"
        state: to_pod

    - name: Restore backup
      kubernetes.core.k8s_exec:
        kubeconfig: "{{ kubeconfig_path }}"
        namespace: "{{ block.config.namespace }}"
        pod: "{{ primary_pod.resources[0].metadata.name }}"
        command: |
          pg_restore \
            -d {{ block.config.aws.rds_database }} \
            -U postgres \
            /tmp/backup.dump

Execution:

# Backup from RDS
export RDS_PASSWORD="your-rds-password"
polycrate run postgres-migration backup-from-rds

# Restore into CloudNativePG
polycrate run postgres-migration restore

# Verification
polycrate run postgres-migration verify

Example 2: complete stack migration

# workspace.poly - full stack migration
name: complete-migration

blocks:
  # Cluster on Linux servers
  - name: cluster
    from: cargo.ayedo.cloud/ayedo/linux/k8s-1.27:0.3.7
    config:
      servers: 3
      agents: 5

  # Databases
  - name: postgres
    from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.3.1
    kubeconfig:
      from: cluster

  - name: redis
    from: cargo.ayedo.cloud/ayedo/k8s/redis:0.1.5
    kubeconfig:
      from: cluster

  # Infrastructure
  - name: ingress
    from: registry.my-org.com/blocks/k8s/nginx:1.0.0
    kubeconfig:
      from: cluster

  - name: cert-manager
    from: registry.my-org.com/blocks/k8s/cert-manager:1.0.0
    kubeconfig:
      from: cluster

  # Monitoring
  - name: monitoring
    from: cargo.ayedo.cloud/ayedo/k8s/victoria-metrics-stack:1.0.0
    kubeconfig:
      from: cluster

  # Registry
  - name: harbor
    from: registry.my-org.com/blocks/k8s/harbor:1.0.0
    kubeconfig:
      from: cluster

workflows:
  - name: migrate-complete-stack
    steps:
      # Infrastructure
      - name: provision-cluster
        block: cluster
        action: provision

      - name: deploy-ingress
        block: ingress
        action: install

      - name: deploy-cert-manager
        block: cert-manager
        action: install

      # Monitoring
      - name: deploy-monitoring
        block: monitoring
        action: install

      # Databases
      - name: deploy-postgres
        block: postgres
        action: install

      - name: deploy-redis
        block: redis
        action: install

      # Registry
      - name: deploy-harbor
        block: harbor
        action: install
# Perform the entire migration
polycrate workflows run migrate-complete-stack --blocks-auto-pull

Advantages of a Polycrate-based migration

1. Data Act compliance

  • ✅ Open standards: Ansible, Kubernetes, OCI images
  • ✅ Machine-readable formats: YAML, JSON
  • ✅ Full portability: workspaces can be moved between providers
  • ✅ No egress fees: block transfer via standard registries
  • ✅ Functional equivalence: K8s-native services offer the same features

2. Sovereignty & control

  • 🏛️ EU hosting: Kubernetes clusters can be operated in Germany/the EU
  • 🔐 Full control: no vendor can force access
  • 🔑 Customer-managed keys: encryption with your own keys
  • 🛡️ DORA/NIS-2 compliant: complete exit strategies documented

3. Cost efficiency

  • 💰 No egress fees: data transfer between your own systems is free
  • 📉 Transparent prices: bare metal/Hetzner instead of AWS markup
  • 🆓 Open-source stack: PostgreSQL instead of RDS, Redis instead of ElastiCache
  • 📊 Predictable costs: no surprises from hidden fees

4. Technical independence

  • 🔄 Provider switch: switch to another Kubernetes provider at any time
  • 🧱 Modular architecture: services can be replaced individually
  • 📦 Standard artifacts: container images, Helm charts, Ansible playbooks
  • 🌍 No vendor binding: open-source ecosystem instead of vendor lock
  • 🛠️ Extensive toolchain: kubectl, helm, ansible & cloud provider CLIs preinstalled

5. Compliance & audit

  • 📋 Documented processes: all migration steps versioned in Git
  • 🔍 Reproducible: exactly the same infrastructure can be rebuilt at any time
  • 📊 Audit trail: complete history of all changes
  • ✅ Certifiable: ISO 27001, BSI IT-Grundschutz compatible

Relation to other concepts

  • Why Polycrate?: fundamental Polycrate philosophy
  • PolyHub: free blocks as hyperscaler alternatives
  • Ansible: industry standard for automation
  • Kubernetes: cloud-agnostic target platform
  • Workflows: orchestration of complex migration processes
  • Registry: OCI-based block sharing for portability
  • Recipes: practical migration examples

Next steps

  1. Perform an assessment: - Install Polycrate: curl -sSL https://get.polycrate.io | bash - Create an inventory workspace with the preinstalled cloud provider tools - Scan AWS/Azure/GCP resources (boto3, Azure SDK, GCP SDK already included) - Perform a cost analysis

  2. Explore the PolyHub: - Visit hub.polycrate.io for K8s-native alternatives - Create a hyperscaler service → PolyHub block mapping

  3. Start a pilot: - Migrate a non-critical service as a proof of concept - Get started right away with the preinstalled tools (kubectl, helm, ansible)

  4. Build the workspace: - Build the target infrastructure in parallel to the hyperscaler - Use PolyHub blocks for Postgres, Redis, monitoring etc.

  5. Plan the data migration: - Define backup/restore workflows with Ansible - Schema export via pg_dump/mongodump (preinstalled)

  6. Perform the cutover: - Redirect traffic to the new infrastructure - Monitoring with the VictoriaMetrics stack

  7. Decommission: - Tear down the old hyperscaler resources - Automate with provider-specific Ansible modules

Ready for sovereign cloud infrastructure?

Contact ayedo for: - Migration assessments - Architecture workshops - Hands-on support with the implementation - Custom block development for specific use cases