Proxmox VEXXI · Migration to ProxmoxCloud to Proxmox
Cloud-to-Proxmox: importing AWS, Azure, and GCP images
What you'll learn
- Pull public cloud images into Proxmox without rebuilding
- Convert cloud disk formats AMI, VHD, raw to Proxmox storage
- Decouple cloud-specific provisioning cloud-init, user-data
- Identify workloads that are NOT worth migrating off-cloud
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-07
The cloud repatriation wave
After a decade of “go cloud first”, many organisations are moving workloads back on-prem. Common drivers:
- Predictable costs — capex vs runaway variable bills
- Latency — colocated systems can’t beat on-prem LAN
- Data gravity — large datasets are expensive to move and egress fees add up
- Compliance — some regulations require data to stay in specific jurisdictions
- Vendor lock-in avoidance — AWS-specific services are hard to leave
This lesson covers how to actually bring cloud images into Proxmox. The tooling differs per provider but the workflow is the same: export, convert, import, decouple cloud-init.
Step 0: Decide if migration is worth it
Not every workload should come back on-prem. Use this rough decision matrix:
| Workload type | Migrate? | Notes |
|---|---|---|
| Stateless web/API tier | Yes | Easy, saves a lot |
| Database on dedicated EC2/EBS | Maybe | Compare RDS cost vs your storage costs |
| Managed database (RDS, Cloud SQL) | Probably not | Refactor to self-hosted DBMS is risky |
| Serverless (Lambda, Cloud Functions) | No | Architecture mismatch |
| Big-data (EMR, BigQuery, Dataproc) | No | Wrong toolset |
| GPU compute (P4, A100) | Maybe | If your on-prem GPU is competitive |
| Internal dev/test VMs | Yes | Big cost saver |
| Legacy monolith VM | Yes | Most common driver |
From AWS
EC2 instances run on EBS volumes (which are essentially snapshots over S3) or instance-store (ephemeral). For migration, you want EBS-backed instances.
Step 1: Find the AMI
# Get the AMI ID from a running instance
aws ec2 describe-instances --instance-ids i-0abc123 --query 'Reservations[].Instances[].ImageId'
# Or list your own AMIs
aws ec2 describe-images --owners self --query 'Images[].[ImageId,Name,CreationDate]'
Step 2: Export the AMI to S3
# Create an S3 bucket and an IAM role for VM Import/Export first
# (See AWS docs for the exact policy)
# Export the AMI as VMDK, VHD, or RAW
aws ec2 export-image \
--image-id ami-0abc123 \
--disk-image-format VMDK \
--s3-export-location S3Bucket=my-export-bucket,S3Prefix=exports/
# Wait for the export task to complete
aws ec2 describe-export-image-tasks --query 'ExportImageTasks[].Status'
Step 3: Download
aws s3 cp s3://my-export-bucket/exports/ami-0abc123.vmdk ./
Step 4: Convert
qemu-img convert -f vmdk -O qcow2 ami-0abc123.vmdk vm-200-disk-0.qcow2
From Azure
Azure VMs use VHD (sometimes VHDX for Gen 2) on managed disks. The export path is straightforward but slow.
Step 1: Deallocate the VM (required for export)
# Stop the VM (do NOT deallocate first if you want the disk state preserved)
# Then snapshot the OS disk:
az snapshot create \
--resource-group myResourceGroup \
--name mySnapshot \
--source /subscriptions/<sub>/resourceGroups/myResourceGroup/providers/Microsoft.Compute/disks/myVM_OsDisk_1_<GUID>
# Get the SAS URL to download the VHD
az snapshot grant-access --name mySnapshot --duration-in-seconds 3600
Step 2: Download
# Use the SAS URL returned above
wget "<SAS_URL>" -O osdisk.vhd
Step 3: Convert
# Azure VHD has 512-byte footer alignment; qemu-img handles it natively
qemu-img convert -f vpc -O qcow2 osdisk.vhd vm-201-disk-0.qcow2
From GCP
GCP supports exporting a custom image to Cloud Storage as a raw disk file. There’s no direct VM export — you create an image from the boot disk first.
Step 1: Create an image from the instance’s boot disk
# Stop the instance first (saves state)
gcloud compute instances stop my-vm --zone us-central1-a
# Create an image
gcloud compute images create my-vm-image \
--source-disk my-vm \
--source-disk-zone us-central1-a \
--family debian-12
Step 2: Export the image
# Create a Cloud Storage bucket first
gsutil mb gs://my-vm-export/
# Export (writes a .tar.gz containing a disk.raw file)
gcloud compute images export \
--destination-uri gs://my-vm-export/my-vm.tar.gz \
--image my-vm-image \
--format raw
Step 3: Download and extract
gsutil cp gs://my-vm-export/my-vm.tar.gz ./
tar -xzf my-vm.tar.gz disk.raw
Step 4: Convert
qemu-img convert -f raw -O qcow2 disk.raw vm-202-disk-0.qcow2
Step 5: Decouple cloud-init
Cloud VMs ship with cloud-init pre-configured. On Proxmox, cloud-init is fine but the datasource must match. If you keep the cloud-init metadata pointing at the cloud provider’s metadata service (169.254.169.254), the guest will hang or misbehave.
On the Proxmox side
Proxmox has built-in cloud-init support that writes an nocloud datasource to a CD-ROM.
qm set 200 --ide2 local-lvm:cloudinit
qm set 200 --ciuser admin
qm set 200 --cipassword 'change-me-now'
qm set 200 --ipconfig0 ip=10.0.0.50/24,gw=10.0.0.1
qm set 200 --sshkeys ~/.ssh/id_rsa.pub
On the guest side
On first boot in Proxmox, cloud-init will read the nocloud CD. The guest’s existing cloud-init config will see a new datasource. To prevent the guest from re-reaching out to AWS/GCP/Azure metadata:
# Inside the guest, disable the cloud-specific datasource
# Debian/Ubuntu
echo "datasource_list: [NoCloud]" > /etc/cloud/cloud.cfg.d/99-pve.cfg
# RHEL/CentOS
echo "datasource_list: [NoCloud]" > /etc/cloud/cloud.cfg.d/99-pve.cfg
# Clean up cloud-init state so it re-runs on first Proxmox boot
cloud-init clean -l
Step 6: License and activation
Cloud VMs typically have:
- Windows — KMS-style activation tied to the cloud provider; needs re-activation with your on-prem KMS
- RHEL — Cloud Access subscription tied to your Red Hat account; bring your entitlement to on-prem
- Ubuntu Pro — token-based; re-attach with
pro attach - SQL Server — may downgrade to evaluation mode; activate with your licence
Plan for these. They are the most common cause of “the migration worked but the VM won’t start its database”.
Step 7: Validate
- Boots cleanly
- Network connectivity (with the Proxmox-set IP, not the cloud one)
- Cloud-init reapplied the right config
- Licence is valid
- Application services start
- Backup runs successfully
- No more calls to
169.254.169.254(check withtcpdumporstrace)
Network reconfiguration
Cloud VMs typically have a single NIC with DHCP from the cloud’s metadata service. In Proxmox:
- Set the IP statically (via cloud-init) or via DHCP (Proxmox doesn’t run a DHCP server by default — set one up or use static IPs)
- Update
/etc/hostsand any service-specific config that references the old cloud name/IP - Update DNS records (split-horizon DNS is a common gotcha; the old cloud IP might still be in DNS)
Bandwidth and timing
A typical “export a VM from cloud” timeline:
| Step | Time |
|---|---|
| AWS export-image task | 5-60 minutes |
| S3 download (100 GB) over 1 Gbps | 15 minutes |
| qemu-img convert | 5-15 minutes |
| Total | 30-90 minutes per VM |
Egress costs from AWS/Azure/GCP can be significant — check the bill before mass-migrating. Often it’s cheaper to keep the source VM running for an extra week and use a separate, faster transfer path (e.g., AWS Snowball, Azure Data Box) for bulk moves.
Key takeaways
- Cloud VMs can be exported, but it takes time and incurs egress fees
- AWS → VMDK, Azure → VHD/VHDX, GCP → raw.tar.gz
- All convert with
qemu-img - Decouple cloud-init from the cloud metadata service before first boot
- Watch for licence re-activation and DNS record updates
- Not every workload is worth bringing back; do the cost math first
Knowledge check
Knowledge check · 4 questions
Q1. Which qemu-img input format is used for an AWS export-image task in VMDK format?
Q2. Which cloud-init datasource should you configure on a Proxmox-imported cloud VM?
Q3. Which command extracts an AWS EC2 image to a local file as VMDK?
Q4. Reconstruct the answer from the lesson context.
Passing score: 75%. Answers are checked in this browser.