Virtual Media Guide¶
This guide explains how to use Redfish virtual media to attach ISO images to KubeVirt virtual machines.
Overview¶
Virtual media allows attaching ISO images to VMs over the network, enabling:
- Operating system installation from ISO
- Booting from recovery media
- Running live systems
KubeVirtBMC implements Redfish virtual media using KubeVirt's DeclarativeHotplugVolumes feature and CDI (Containerized Data Importer).
Prerequisites¶
1. CDI Installation¶
CDI (Containerized Data Importer) must be installed:
# Check if CDI is installed
kubectl get crd datavolumes.cdi.kubevirt.io
# If not installed, see: https://github.com/kubevirt/containerized-data-importer
2. KubeVirt Feature Gate¶
The DeclarativeHotplugVolumes feature gate must be enabled:
# Check KubeVirt configuration
kubectl get kubevirt kubevirt -n kubevirt -o yaml | grep DeclarativeHotplugVolumes
If the feature gate is not enabled, enable it using the patch command:
kubectl patch kubevirt kubevirt -n kubevirt --type merge -p '{"spec":{"configuration":{"developerConfiguration":{"featureGates":["DeclarativeHotplugVolumes"]}}}}'
Ensure it's enabled in KubeVirt spec:
3. VirtualMachine Configuration¶
Your VirtualMachine must have a CD-ROM disk defined:
kubectl apply -f - <<EOF
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: testvm
namespace: default
spec:
runStrategy: Always
template:
spec:
domain:
devices:
disks:
- name: containerdisk
disk:
bus: virtio
# CD-ROM disk required for virtual media
- cdrom:
bus: sata
name: cdrom
interfaces:
- name: default
masquerade: {}
resources:
requests:
memory: 64Mi
volumes:
- name: containerdisk
containerDisk:
image: quay.io/kubevirt/cirros-container-disk-demo
networks:
- name: default
pod: {}
EOF
Important:
- CD-ROM disk must exist in spec before using virtual media
- Bus type should be
sata(recommended) orscsi - Disk name can be any value (e.g.,
cdrom,iso,dvd)
Storage Overhead¶
If you are using a storage backend with higher filesystem overhead than CDI's default assumption of 6% (e.g. Ceph RBD which can have ~9.5% overhead), you may still encounter the following error:
In this case, you need to increase the filesystemOverhead in your CDI
config to match your storage backend's actual overhead. Set the value to a
percentage slightly above your backend's real overhead (e.g. 0.15 for 15%,
which gives a safe buffer for Ceph RBD):
kubectl patch cdi cdi --type=merge -p \
'{"spec":{"config":{"filesystemOverhead":{"global":"0.15"}}}}'
Note
This tells CDI to allocate a larger scratch PVC during import, ensuring there is enough usable space after the storage backend takes its overhead cut.
Verify it applied:
Inserting Virtual Media¶
Using Redfish API¶
# First, create a session and get token
TOKEN=$(curl -s -i -X POST \
-H "Content-Type: application/json" \
http://testvm-virtbmc.default.svc/redfish/v1/SessionService/Sessions \
-d '{"UserName":"admin","Password":"admin123"}' \
| grep -i "X-Auth-Token" | cut -d' ' -f2 | tr -d '\r')
# Insert virtual media
curl -i -X POST \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
http://testvm-virtbmc.default.svc/redfish/v1/Managers/BMC/VirtualMedia/CD1/Actions/VirtualMedia.InsertMedia \
-d '{
"Image": "https://releases.ubuntu.com/noble/ubuntu-24.04.3-live-server-amd64.iso",
"Inserted": true
}'
Ejecting before re-inserting
InsertMedia and EjectMedia are Redfish POST actions, which are
non-idempotent by definition. If media is already inserted, call
VirtualMedia.EjectMedia before issuing another VirtualMedia.InsertMedia
request. Calling InsertMedia again while media is already attached is
undefined behavior and should not be used as a retry or polling mechanism.
Booting from Virtual Media¶
After inserting virtual media, a DataVolume is created and the ISO image begins downloading. Once the download is complete, configure the VM to boot from the CD-ROM and restart it:
InsertMedia is asynchronous
InsertMedia returns as soon as the DataVolume is created — it does
not wait for the ISO to finish downloading. KubeVirtBMC forces the
import to start immediately (via the
cdi.kubevirt.io/storage.bind.immediate.requested annotation), so this
works even on stopped VMs and WaitForFirstConsumer storage classes.
Power-on requests are accepted immediately, but the VM's pod won't
schedule until the DataVolume reaches Succeeded — so provisioning
tools with power-on timeouts may time out waiting for the VM to come
up. Wait for Succeeded (below) before powering on to avoid this.
# Watch the DataVolume download progress
kubectl get datavolume testvm -n default -w
# Set boot override to CD-ROM
curl -i -X PATCH \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
http://testvm-virtbmc.default.svc/redfish/v1/Systems/1 \
-d '{
"Boot": {
"BootSourceOverrideTarget": "Cd",
"BootSourceOverrideEnabled": "Once"
}
}'
# Restart the VM to boot from CD-ROM
curl -i -X POST \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
http://testvm-virtbmc.default.svc/redfish/v1/Systems/1/Actions/ComputerSystem.Reset \
-d '{"ResetType":"GracefulRestart"}'
In-guest reboot handling
If the guest OS reboots itself (e.g., an installer rebooting after it finishes), KubeVirt by default restarts the guest inside the existing VMI, which still carries the CD-ROM-first boot order it was created with — so the VM boots from the virtual media again instead of from the freshly installed disk. Setting rebootPolicy: Terminate on the KubeVirt VirtualMachine makes guest reboots recreate the VMI from the VM template, which KubeVirtBMC has already restored to the default boot order when the one-shot was consumed, so the VM boots from disk as expected. See the Redfish Guide for details. This requires KubeVirt 1.8.0+ with the RebootPolicy feature gate enabled.
Request Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
Image |
string | Yes | HTTP/HTTPS URL to ISO image |
Inserted |
boolean | Yes | Set to true to insert media |
Supported Image Sources¶
- HTTP URLs:
http://example.com/image.iso - HTTPS URLs:
https://example.com/image.iso - Public repositories: Ubuntu, CentOS, etc.
- Internal services: Services accessible from the cluster
Example ISO URLs¶
# Ubuntu 24.04
"https://releases.ubuntu.com/noble/ubuntu-24.04.3-live-server-amd64.iso"
# CentOS Stream
"https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/iso/CentOS-Stream-9-latest-x86_64-dvd1.iso"
# Internal service
"http://internal-iso-service.default.svc/image.iso"
Checking Status¶
Get Virtual Media Status¶
curl -H "X-Auth-Token: $TOKEN" \
http://testvm-virtbmc.default.svc/redfish/v1/Managers/BMC/VirtualMedia/CD1 | jq
Response includes:
Image: The ISO URLInserted: Whether media is currently insertedMediaTypes: Supported types (CD, DVD)
Ejecting Virtual Media¶
Eject Virtual Media¶
curl -i -X POST \
-H "Content-Type: application/json" \
-H "X-Auth-Token: $TOKEN" \
http://testvm-virtbmc.default.svc/redfish/v1/Managers/BMC/VirtualMedia/CD1/Actions/VirtualMedia.EjectMedia \
-d '{}'
Note: The request body must be an empty JSON object {}.
Next Steps¶
- Read the Redfish Guide for complete Redfish API usage