Version: v2.1.0 Date: 2026-01-31 Status: Production Ready
This integration enables seamless migration of VMware VMs to KubeVirt-managed virtual machines running on Kubernetes.
Flow:
VMware VMDK → Hyper2KVM Conversion → KubeVirt VM → Running on K8s
Benefits:
┌─────────────────────────────────────────────────────────┐
│ KubeVirt VirtualMachine (VM running in pod) │
├─────────────────────────────────────────────────────────┤
│ DataVolume (QCOW2 image in PVC) │
├─────────────────────────────────────────────────────────┤
│ Hyper2KVM Conversion (VMDK → QCOW2 + offline fixes) │
├─────────────────────────────────────────────────────────┤
│ Source VMDK (VMware export) │
└─────────────────────────────────────────────────────────┘
Hardware:
/dev/kvm available on nodes)Software:
# Install KubeVirt operator
export VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases/latest | grep tag_name | cut -d '"' -f 4)
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml
# Wait for deployment
kubectl wait --for=condition=Available kubevirt kubevirt -n kubevirt --timeout=5m
# Install virtctl CLI
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-linux-amd64
chmod +x virtctl
sudo mv virtctl /usr/local/bin/
export CDI_VERSION=$(curl -s https://github.com/kubevirt/containerized-data-importer/releases/latest | grep -o "v[0-9]\+\.[0-9]\+\.[0-9]\+")
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-operator.yaml
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-cr.yaml
# Install CRDs
kubectl apply -f https://raw.githubusercontent.com/ssahani/hyper2kvm/main/k8s/operator/crds/
# Deploy operator (see DEPLOYMENT.md)
kubectl apply -f k8s/operator/manifests/
Create a MigrationJob to convert the VMDK:
apiVersion: hyper2kvm.io/v1alpha1
kind: MigrationJob
metadata:
name: centos9-conversion
namespace: vms
spec:
operation: offline_fix # Full conversion with KVM preparation
image:
path: /data/input/centos9.vmdk
format: vmdk
artifacts:
output_path: /data/output
output_format: qcow2
compress: true
priority: 80
timeout: 90m
# Offline fix parameters
parameters:
fstab_mode: stabilize-all
regen_initramfs: true
regenerate_grub: true
network_fixes: moderate
disable_vmware_services: true
Apply:
kubectl apply -f centos9-migration.yaml
# Watch progress
kubectl get migrationjob centos9-conversion -n vms -w
# Check output
kubectl exec <worker-pod> -n vms -- ls -lh /data/output/
Import the QCOW2 into a DataVolume:
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: centos9-disk
namespace: vms
spec:
source:
pvc:
name: centos9-output-pvc # PVC from MigrationJob
namespace: vms
storage:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi # Adjust based on VM size
storageClassName: standard
Alternative: HTTP source (if QCOW2 is hosted):
spec:
source:
http:
url: "https://storage.example.com/centos9-fixed.qcow2"
storage:
# ... same as above
Define the VM using the converted disk:
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: centos9-vm
namespace: vms
labels:
app: centos9
migrated-by: hyper2kvm
spec:
running: false # Set to true to auto-start
template:
metadata:
labels:
kubevirt.io/vm: centos9-vm
spec:
domain:
devices:
disks:
- name: rootdisk
disk:
bus: virtio # Hyper2KVM injected virtio drivers
interfaces:
- name: default
masquerade: {}
model: virtio
resources:
requests:
memory: 2Gi
cpu: 2
networks:
- name: default
pod: {}
volumes:
- name: rootdisk
dataVolume:
name: centos9-disk
Apply:
kubectl apply -f centos9-vm.yaml
# Start the VM
virtctl start centos9-vm -n vms
# Check status
kubectl get vmi -n vms
virtctl console centos9-vm -n vms
---
# Step 1: Namespace
apiVersion: v1
kind: Namespace
metadata:
name: vm-migrations
---
# Step 2: PVC for VMDK input
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: centos9-vmdk-input
namespace: vm-migrations
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
# Step 3: PVC for QCOW2 output
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: centos9-qcow2-output
namespace: vm-migrations
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
# Step 4: MigrationJob (converts VMDK → QCOW2)
apiVersion: hyper2kvm.io/v1alpha1
kind: MigrationJob
metadata:
name: centos9-migration
namespace: vm-migrations
spec:
operation: offline_fix
image:
path: /data/input/centos9.vmdk
format: vmdk
artifacts:
output_path: /data/output
output_format: qcow2
compress: true
priority: 90
timeout: 120m
parameters:
fstab_mode: stabilize-all
regen_initramfs: true
regenerate_grub: true
network_fixes: moderate
---
# Step 5: DataVolume (imports QCOW2 for KubeVirt)
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: centos9-rootdisk
namespace: vm-migrations
spec:
source:
pvc:
name: centos9-qcow2-output
namespace: vm-migrations
storage:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: standard
---
# Step 6: VirtualMachine (runs converted image)
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: centos9
namespace: vm-migrations
labels:
app: centos9
os: centos-stream-9
migrated-from: vmware
migrated-by: hyper2kvm
migration-date: "2026-01-31"
annotations:
description: "CentOS Stream 9 migrated from VMware"
hyper2kvm/source-vmdk: "centos9.vmdk"
hyper2kvm/conversion-job: "centos9-migration"
spec:
running: true
template:
metadata:
labels:
kubevirt.io/vm: centos9
spec:
domain:
cpu:
cores: 2
devices:
disks:
- name: rootdisk
disk:
bus: virtio
- name: cloudinit
disk:
bus: virtio
interfaces:
- name: default
masquerade: {}
model: virtio
machine:
type: q35
resources:
requests:
memory: 2Gi
networks:
- name: default
pod: {}
volumes:
- name: rootdisk
dataVolume:
name: centos9-rootdisk
- name: cloudinit
cloudInitNoCloud:
userData: |
#cloud-config
hostname: centos9
users:
- name: centos
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAA... # Add your SSH key
# Create namespace
kubectl create namespace vm-migrations
# Upload VMDK to PVC
kubectl run vmdk-uploader -n vm-migrations \
--image=busybox --restart=Never \
--overrides='{"spec":{"containers":[{"name":"uploader","image":"busybox","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/data"}]}],"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"centos9-vmdk-input"}}]}}'
kubectl cp /path/to/centos9.vmdk vm-migrations/vmdk-uploader:/data/centos9.vmdk
kubectl delete pod vmdk-uploader -n vm-migrations
kubectl apply -f complete-vm-migration.yaml
# Monitor conversion
kubectl get migrationjob -n vm-migrations -w
# Monitor DataVolume import
kubectl get datavolume -n vm-migrations -w
# Check VM status
kubectl get vm -n vm-migrations
kubectl get vmi -n vm-migrations
# Start VM (if not auto-started)
virtctl start centos9 -n vm-migrations
# Access console
virtctl console centos9 -n vm-migrations
# SSH access (if cloud-init configured)
virtctl ssh centos@centos9 -n vm-migrations
# VNC access
virtctl vnc centos9 -n vm-migrations
Enable live migration between nodes:
spec:
template:
spec:
evictionStrategy: LiveMigrate
Trigger migration:
virtctl migrate centos9 -n vm-migrations
apiVersion: snapshot.kubevirt.io/v1alpha1
kind: VirtualMachineSnapshot
metadata:
name: centos9-snapshot-1
namespace: vm-migrations
spec:
source:
apiGroup: kubevirt.io
kind: VirtualMachine
name: centos9
spec:
template:
spec:
domain:
devices:
disks:
- name: rootdisk
disk:
bus: virtio
- name: datadisk
disk:
bus: virtio
volumes:
- name: rootdisk
dataVolume:
name: centos9-root
- name: datadisk
dataVolume:
name: centos9-data
apiVersion: hyper2kvm.io/v1alpha1
kind: MigrationJob
metadata:
name: vm-migration-with-kubevirt
namespace: vm-migrations
spec:
operation: offline_fix
image:
path: /data/input/windows10.vmdk
format: vmdk
artifacts:
output_path: /data/output
output_format: qcow2
# KubeVirt integration (custom extension)
kubevirt:
enabled: true
vmName: windows10
dataVolumeName: windows10-disk
autoStart: false
vmSpec:
resources:
requests:
memory: 4Gi
cpu: 4
cloudInit:
enabled: false # Windows doesn't support cloud-init
# Hyper2KVM conversion
kubectl get migrationjob -n vm-migrations
kubectl describe migrationjob centos9-migration -n vm-migrations
# DataVolume import
kubectl get dv -n vm-migrations
kubectl describe dv centos9-rootdisk -n vm-migrations
# VM status
kubectl get vm,vmi -n vm-migrations
virtctl info centos9 -n vm-migrations
# Hyper2KVM worker logs
kubectl logs -n vm-migrations <migration-worker-pod>
# KubeVirt virt-launcher logs
kubectl logs -n vm-migrations <virt-launcher-pod>
# CDI importer logs
kubectl logs -n vm-migrations <importer-pod>
Symptoms:
Fix: Ensure offline fixes were applied during conversion:
spec:
parameters:
regen_initramfs: true # Essential for virtio drivers
regenerate_grub: true # Essential for boot
fstab_mode: stabilize-all # Essential for filesystem mounts
Check:
virtctl console centos9 -n vm-migrations
# Inside VM
ip a
Fix: Ensure network was fixed during conversion:
spec:
parameters:
network_fixes: moderate # Updates NetworkManager configs
Check:
kubectl get events -n vm-migrations --sort-by='.lastTimestamp'
kubectl describe dv centos9-rootdisk -n vm-migrations
Common causes:
For best VM performance:
spec:
storage:
storageClassName: fast-ssd # High-performance SSD
spec:
template:
spec:
domain:
cpu:
cores: 4
dedicatedCpuPlacement: true # CPU pinning
memory:
hugepages:
pageSize: 2Mi # Use hugepages
resources:
requests:
memory: 8Gi
limits:
memory: 8Gi
Always validate converted images:
# Check QCOW2 integrity
qemu-img check centos9-fixed.qcow2
# Scan for malware (if applicable)
clamscan centos9-fixed.qcow2
Restrict VM network access:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: vm-network-policy
namespace: vm-migrations
spec:
podSelector:
matchLabels:
kubevirt.io/vm: centos9
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
role: admin
egress:
- to:
- podSelector:
matchLabels:
role: database
| Aspect | Traditional Migration | Hyper2KVM + KubeVirt |
|---|---|---|
| Conversion | Manual qemu-img | Automated with offline fixes |
| Driver injection | Manual | Automatic (virtio drivers) |
| Boot config | Manual GRUB/fstab edits | Automatic |
| Deployment | VM install, manual config | Declarative YAML |
| Scaling | Manual, per-VM | Kubernetes-native |
| Storage | Separate management | Integrated PVCs |
| Networking | Separate SDN | Kubernetes CNI |
| Monitoring | Separate tools | Kubernetes-native |
| HA | Complex setup | Built-in (live migration) |
Before production deployment:
Successful migrations:
Guest OS Support:
Cluster Platforms:
Last Updated: 2026-01-31 Status: Production Ready Integration Tested: ✅ CentOS 9 local conversion validated KubeVirt Compatibility: v1.0+