Skip to content

Use Cases

These examples start from common VM workflows. Replace image IDs, ports, disk sizes, and interface names for your environment.

Temporary VM

Use this when you want to try a VM and discard it when the container exits.

docker run --rm -it \
  --device /dev/kvm \
  -e DISTRO=alpine-3.22-cloud-amd64 \
  ghcr.io/munenick/docker-vm-runner:latest

Persistent VM

Use /data when you want to keep the VM disk and image cache.

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

Sized Cloud VM

Use this when the default CPU, memory, or disk size is too small.

docker run --rm -it \
  --device /dev/kvm \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e CPUS=4 \
  -e MEMORY=8192 \
  -e DISK_SIZE=40G \
  -v ubuntu-dev-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

ISO Install To Disk

Use this when you want to install from an ISO and boot the installed VM later.

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

After the persistent VM stops, the next run boots from the installed disk. Set FORCE_ISO=1 when you need to attach the ISO again.

Rescue Or Repeat ISO Boot

Use FORCE_ISO=1 for rescue media, live images, or repeated installer boots.

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

Background SSH VM

Use -dit when you want the VM to keep running and connect with SSH.

docker run -dit --name ubuntu-ssh \
  --device /dev/kvm \
  -p 2222:2222 \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e GUEST_NAME=ubuntu-ssh \
  -e SSH_PUBKEY="$(cat ~/.ssh/id_ed25519.pub)" \
  -v ubuntu-ssh-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Connect with:

ssh user@localhost -p 2222

Use docker attach ubuntu-ssh when you need the serial console. Press Ctrl+P then Ctrl+Q to detach without stopping the VM.

Run Commands In The VM Without Login

Use guest-exec when you want to run a command inside the VM without opening the console or enabling SSH.

Start a persistent VM in the background:

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

Run a command inside the VM from the host:

docker exec command-vm guest-exec --wait "uname -a"

Run a package or system command inside the VM without running it on the host:

docker exec command-vm guest-exec --wait "apt-get update"

Use argv form when you do not need shell features:

docker exec command-vm guest-exec --wait systemctl is-system-running

This is useful for OS-level checks, package-manager operations, service probes, and test commands that should affect the VM rather than the host.

For command syntax and guest-agent requirements, see Access.

Browser Console For GUI Installers

Use noVNC when the installer or desktop workflow needs a graphical console.

docker run --rm -it \
  --device /dev/kvm \
  -p 6080:6080 \
  -e GRAPHICS=novnc \
  -e BOOT_FROM=https://example.com/desktop-installer.iso \
  -e GUEST_NAME=desktop-install \
  -e MEMORY=8192 \
  -e DISK_SIZE=80G \
  -v desktop-install-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Open:

https://localhost:6080/

Server VM With Extra Ports

Use PORT_FWD when a service inside the VM should be reachable from the host.

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

This forwards host localhost:8080 to guest port 80 through the default NAT network.

LAN VM With Bridge

Use Bridge when the host already has a bridge such as br0 and the VM should receive an address from that network.

docker run -d --name bridge-vm \
  --network host \
  --cap-add NET_ADMIN \
  --device /dev/kvm \
  --device /dev/net/tun \
  --device /dev/vhost-net \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e NETWORK_MODE=bridge \
  -e NETWORK_BRIDGE=br0 \
  -e NO_CONSOLE=1 \
  -v bridge-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Connect to the guest through the address it receives on the bridged network.

Physical Network VM With Direct

Use Direct when the VM needs a real address through a physical NIC but you do not want to create a host bridge.

MACVTAP_MAJOR="$(awk '$2 == "macvtap" { print $1 }' /proc/devices)"
if [ -z "$MACVTAP_MAJOR" ]; then
  echo "macvtap is not available on this host" >&2
  exit 1
fi

docker run -d --name direct-vm \
  --network host \
  --cap-add NET_ADMIN \
  --device /dev/kvm \
  --device /dev/vhost-net \
  --device-cgroup-rule "c ${MACVTAP_MAJOR}:* rwm" \
  -v /dev:/dev:ro \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e NETWORK_MODE=direct \
  -e NETWORK_DIRECT_DEV=eth0 \
  -e NO_CONSOLE=1 \
  -v direct-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Replace eth0 with the host interface from ip -br link.

Network Boot With iPXE

Use iPXE when the VM should boot from your network boot 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

Use the bridge or direct network mode that reaches your DHCP/TFTP/HTTP boot services.

Share A Host Directory

Use filesystem sharing when the VM should read or write a host directory.

mkdir -p share

docker run --rm -it \
  --device /dev/kvm \
  -v "$PWD/share:/shared" \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e FILESYSTEM_SOURCE=/shared \
  -e FILESYSTEM_TARGET=shared \
  -e FILESYSTEM_DRIVER=9p \
  -e FILESYSTEM_ACCESSMODE=mapped \
  -v shared-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Inside the guest:

sudo mkdir -p /mnt/shared
sudo mount -t 9p -o trans=virtio,version=9p2000.L shared /mnt/shared

VM With Extra Disk

Use an extra disk when application data or test data should be separate from the boot disk.

docker run --rm -it \
  --device /dev/kvm \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e DISK_SIZE=40G \
  -e DISK_TYPE=scsi \
  -e DISK2_SIZE=100G \
  -v storage-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

The guest sees the extra disk as another block device.

Redfish-Controlled VM

Use Redfish when another tool should control VM power or boot behavior through a BMC-like API.

docker run --rm -it \
  --device /dev/kvm \
  -p 8443:8443 \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e REDFISH_ENABLE=1 \
  -e REDFISH_PASSWORD='change-me' \
  -v redfish-vm-data:/data \
  ghcr.io/munenick/docker-vm-runner:latest

Check the endpoint:

curl -k -u admin:change-me https://localhost:8443/redfish/v1/

Preview Before Starting

Use --show-config or --dry-run before starting a complex VM.

docker run --rm \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e CPUS=4 \
  -e MEMORY=8192 \
  -e NETWORK_MODE=nat \
  -e PORT_FWD=8080:80 \
  ghcr.io/munenick/docker-vm-runner:latest --show-config

Validate without starting:

docker run --rm \
  -e DISTRO=ubuntu-24.04-cloud-amd64 \
  -e CPUS=4 \
  -e MEMORY=8192 \
  ghcr.io/munenick/docker-vm-runner:latest --dry-run