Skip to main content
A VM workload boots from a disk image. There are two ways to provide one:
  • An OCI containerDisk — a disk image packaged as a normal OCI image and pushed to a registry. Best for reproducibility and reuse.
  • An HTTP(S) disk image — a disk file (qcow2, raw, VMDK, VHD, …) hosted at a URL and imported into a persistent disk on first boot. Best for large images or a one‑time “lift and shift.”
This guide covers building and publishing both, and converting images that start in another format.

Choosing an approach

OCI containerDisk

Reproducible, versioned, and pulled like any other image. Recommended for images you reuse or roll out across many VMs.

HTTP(S) import

No image build step. Point at a URL; Control Plane imports and converts it into a persistent disk. Good for large or one-off images.

Supported source formats

The HTTP(S) importer accepts and auto‑converts these disk formats:
FormatExtensionsNotes
QEMU copy‑on‑write.qcow2Recommended; compact and sparse.
Raw.img, .rawUncompressed full‑size disk.
VMware.vmdkVMware exports.
Hyper‑V.vhd, .vhdxWindows/Hyper‑V exports.
VirtualBox.vdiVirtualBox exports.
Optical.isoCD/DVD media.
Compressed variants (.gz, .xz) are also accepted and decompressed on import. Both the OCI containerDisk and HTTP(S) paths run the same conversion — CDI converts the source to the boot disk’s native format on import — so you can ship a .vmdk (or .vhd, .vdi, …) directly without converting it first. Converting to qcow2 at build time (see Converting images) is an optional optimization that moves the one-time conversion cost out of the import.

Option A — Publish an OCI containerDisk

A containerDisk is an OCI image whose only contents are a single disk file placed in /disk/. KubeVirt loads that disk as the VM’s boot device.
1

Obtain a disk image

Use any supported formatqcow2, raw, vmdk, vhd/vhdx, or vdi. CDI converts it to the boot disk’s native format on import, so no pre-conversion is needed. (Converting to qcow2 first is optional — see Converting images.)
2

Write a Dockerfile

The image is built FROM scratch with the disk added to /disk/. The disk file must be readable by UID 107 (the qemu user that runs the VM), so set ownership with --chown=107:107:
Dockerfile
FROM scratch
ADD --chown=107:107 my-disk.vmdk /disk/
Nothing else belongs in a containerDisk — no base OS, no entrypoint. The disk file alone is the payload (any supported format). Omitting --chown=107:107 causes a permission error when KubeVirt tries to open the disk.
3

Build and push to your registry

Build for linux/amd64 and push to your org’s registry. Using the CLI:
cpln image build --name my-vm-disk:v1 --push
Or with Docker directly (registry path ORG.registry.cpln.io/IMAGE:TAG):
cpln image docker-login
docker buildx build --platform=linux/amd64 \
  -t my-org.registry.cpln.io/my-vm-disk:v1 .
docker push my-org.registry.cpln.io/my-vm-disk:v1
See Push Images to Registry for authentication and CI/CD details.
4

Reference it in the VM workload

Use the Control Plane image link format — //image/<name>:<tag> or /org/<org>/image/<name>:<tag>:
YAML
spec:
  type: vm
  vm:
    bootDisk:
      source:
        oci:
          image: //image/my-vm-disk:v1
Public containerDisks work too, e.g. quay.io/containerdisks/ubuntu:22.04.
A VM’s boot disk is always backed by a volume set, so bootDisk.persist.volumeSet is required alongside the source. See Persistence.

Option B — Boot from an HTTP(S) image

Host the disk image at an HTTP(S) URL and let Control Plane import it. The import lands in a per‑replica persistent disk, so a boot volume set is required.
1

Host the image

Place the disk file (any supported format) behind an http(s) URL the cluster can reach — an object store signed URL, a release asset, or a simple web server.
2

(Recommended) Compute a checksum

sha256sum my-disk.qcow2
3

Reference it with a boot volume set

YAML
spec:
  type: vm
  vm:
    bootDisk:
      source:
        http:
          url: https://example.com/images/my-disk.qcow2
          checksum: 'sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
      persist:
        volumeSet: cpln://volumeset/my-vm-root
On first boot the image is downloaded, verified against the checksum, converted to the disk’s native format, and written to the replica’s persistent disk. Subsequent boots reuse the imported disk.
Object‑store schemes (s3://, gs://) are not yet accepted directly — use a signed http(s) URL instead.

Converting images

CDI converts disk formats for you on import, so this step is optional — reach for it only to shrink an image or to pay the conversion cost once at build time instead of on every import. qemu-img (from the qemu-utils package) converts between formats:
qemu-img convert -p -O qcow2 disk.vmdk disk.qcow2
Inspect an image to confirm its format and virtual size:
qemu-img info disk.qcow2
To shrink a sparse disk after conversion, run virt-sparsify --in-place disk.qcow2 (from libguestfs-tools). Smaller images push and import faster.

Building a containerDisk from a converted image

Once converted, package and push it as in Option A:
Dockerfile
FROM scratch
ADD --chown=107:107 disk.qcow2 /disk/
cpln image build --name my-vm-disk:v1 --push

Windows images

Windows guests work the same way, with a few practicalities:
  • Set spec.vm.guestOS: windows so the correct DNS bootstrap is injected. See Cloud-init & platform injection.
  • Windows disk images can be large; the HTTP import path avoids building a multi‑GB OCI image.
  • A generation‑1 (MBR) VHD boots with firmware.bootloader: bios; a UEFI image uses the default efi.
  • Ensure VirtIO drivers are present in the image so the guest sees its disk and network. Microsoft’s evaluation VHDs typically need the VirtIO drivers slipstreamed in before upload.
YAML
spec:
  type: vm
  vm:
    guestOS: windows
    bootDisk:
      source:
        http:
          url: https://example.com/images/windows-server-2022.vhd
      persist:
        volumeSet: cpln://volumeset/win-root
    firmware:
      bootloader: bios

Next steps

VM Workloads

Configure and run the VM

Volume Sets

Persist boot and data disks

Push Images

Registry authentication and CI/CD

Create a Workload

Deploy from a manifest