Skip to content

Boot

Boot settings control what the VM starts from: a catalog disk, an installer ISO, a local image, a blank disk, or a network boot path.

Default Boot

By default, docker-vm-runner starts the catalog image selected by DISTRO:

docker run --rm -it \
  --device /dev/kvm \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -v docker-vm-runner-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Cloud images usually boot directly from disk and use cloud-init for the default user, password, and SSH key setup.

Boot From A Custom Source

Use BOOT_FROM for a URL, local path, OCI reference, ISO, disk image, or blank.

Remote disk image:

docker run --rm -it \
  --device /dev/kvm \
  -e BOOT_FROM=https://example.com/image.qcow2 \
  -v docker-vm-runner-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Local disk image:

docker run --rm -it \
  --device /dev/kvm \
  -v "$PWD/images:/boot-media:ro" \
  -e BOOT_FROM=/boot-media/image.qcow2 \
  -v docker-vm-runner-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Blank disk:

docker run --rm -it \
  --device /dev/kvm \
  -e BOOT_FROM=blank \
  -e DISK_SIZE=40G \
  -v blank-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

ISO Install

Use an ISO when you want to run an installer. ISO boot creates a blank working disk by default and puts cdrom first in the boot order.

docker run --rm -it \
  --device /dev/kvm \
  -e BOOT_FROM=https://example.com/installer.iso \
  -e GUEST_NAME=installed-vm \
  -e CPUS=4 \
  -e MEMORY=8192 \
  -e DISK_SIZE=80G \
  -v installed-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

When an ISO-backed persistent VM stops after installation, docker-vm-runner treats it as installed. The next run reuses the VM disk and boots from disk instead of attaching the ISO again.

Keep the ISO attached when you need to boot the installer or rescue media again:

docker run --rm -it \
  --device /dev/kvm \
  -e BOOT_FROM=https://example.com/installer.iso \
  -e FORCE_ISO=1 \
  -v installed-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Cloud-init is disabled automatically for ISO boot. Set CLOUD_INIT=1 only when the ISO workflow expects cloud-init data.

Boot Order

Use BOOT_ORDER to choose the boot priority. Supported values are hd, cdrom, and network.

docker run --rm \
  -e BOOT_ORDER=network,hd \
  ghcr.io/munenick/docker-vm-runner:latest --show-config

When BOOT_FROM points to an ISO, cdrom is automatically added before the disk unless you choose a different flow after installation.

Boot Mode

Use BOOT_MODE for firmware selection:

Value Use case
uefi Default modern boot mode.
legacy BIOS-style boot for older images or network boot environments.
secure Secure Boot with TPM enabled by default.

Example:

docker run --rm -it \
  --device /dev/kvm \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e BOOT_MODE=secure \
  -v secure-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

iPXE Network Boot

Use iPXE when the VM should network boot from your DHCP/TFTP/HTTP infrastructure.

docker run --rm -it \
  --network host \
  --cap-add NET_ADMIN \
  --device /dev/kvm \
  --device /dev/net/tun \
  --device /dev/vhost-net \
  -e IPXE_ENABLE=1 \
  -e BOOT_ORDER=network,hd \
  -e NETWORK_MODE=bridge \
  -e NETWORK_BRIDGE=br0 \
  -e GUEST_NAME=ipxe-vm \
  -v ipxe-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

IPXE_ENABLE=1 injects an iPXE ROM into the primary NIC and moves network to the front of the boot order.

Bridge or Direct networking is the practical choice for real PXE environments because the guest needs to reach your upstream DHCP/TFTP/HTTP services. NAT can be used for simple tests, but it relies on user-mode networking and is usually not the right path for infrastructure PXE.

Custom iPXE ROM

The image includes iPXE ROMs from ipxe-qemu for common NIC models. Provide IPXE_ROM_PATH when you want to use your own ROM or an architecture/model combination without a bundled ROM.

docker run --rm -it \
  --device /dev/kvm \
  -v "$PWD/ipxe:/ipxe:ro" \
  -e IPXE_ENABLE=1 \
  -e IPXE_ROM_PATH=/ipxe/custom.rom \
  -e GUEST_NAME=ipxe-vm \
  -v ipxe-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

The default ROM is selected from the VM architecture and NETWORK_MODEL. For example, x86_64 with the default virtio NIC uses /usr/share/qemu/pxe-virtio.rom.

iPXE Script Example

Host an iPXE script on your network and point your DHCP/TFTP environment at it. A minimal script can chainload a kernel and initramfs:

#!ipxe
dhcp
set base-url http://boot.example.com/images/alpine
kernel ${base-url}/vmlinuz alpine_repo=${base-url}/repo
initrd ${base-url}/initramfs
boot