vmrunner 0.0.4

micro-vm runner for testcases that require root or invasive IO
docs.rs failed to build vmrunner-0.0.4
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: vmrunner-0.0.3

vmrunner

VM-backed Rust tests with libkrun.

Requirements

Generic:

  • libkrun and libkrunfw
  • Rust 1.85 or newer
  • a Linux root qcow2 image or HTTP(S) image URL
  • a full Rust guest target, for example aarch64-unknown-linux-gnu
  • curl when root_qcow2 uses an HTTP(S) URL

Linux:

  • /dev/kvm access
  • unshare(1) in PATH with --user --map-root-user --net --fork
  • user and network namespaces enabled
  • /dev/net/tun access for TunTap

macOS:

  • macOS 14 or newer on Apple Silicon
  • Apple Hypervisor.framework
  • gvproxy for host or internet uplinks
  • Zig when CC_LINUX is unset

Setup

macOS:

brew tap libkrun/krun
brew trust libkrun/krun
brew install libkrun/krun/libkrun libkrun/krun/gvproxy zig rustup
rustup toolchain install stable

Linux: install libkrun, libkrunfw, rustup, util-linux, curl, and optional zig with your distro package manager.

Guest target examples:

  • Apple Silicon or Linux aarch64: aarch64-unknown-linux-gnu
  • Linux x86_64: x86_64-unknown-linux-gnu

FreeBSD guests are temporarily disabled. Use Linux guest targets for now.

vmrunner rejects guest targets whose Rust target arch does not match the host arch. If guest_target is omitted, the macro uses <host-arch>-unknown-linux-musl.

rustup target add aarch64-unknown-linux-gnu
export KRUN_INIT_GUEST_TARGET=aarch64-unknown-linux-gnu
export CC_LINUX="zig cc -target aarch64-unknown-linux-gnu"

Host target map:

#[vmrunner::test(
    root_qcow2 = "https://cloud-images.ubuntu.com/daily/server/releases/26.04/release-20260720/ubuntu-26.04-server-cloudimg-arm64.img",
    guest_target = [
        "aarch64-apple-darwin" | "aarch64-unknown-linux-gnu" => "aarch64-unknown-linux-musl",
        "x86_64-unknown-linux-gnu" => "x86_64-unknown-linux-musl",
    ],
)]
fn mapped(setup: vmrunner::TestSetup) -> anyhow::Result<()> {
    Ok(())
}

No map match prints a warning and picks a target with the same arch when possible. Otherwise it falls back to <host-arch>-unknown-linux-musl.

Ubuntu cloud image example

URL roots are downloaded once to target/vmrunner-qcow2-imag/<file>.

rustup target add aarch64-unknown-linux-gnu
export KRUN_INIT_GUEST_TARGET=aarch64-unknown-linux-gnu
export CC_LINUX="zig cc -target aarch64-unknown-linux-gnu"
cargo run -p vmrunner --bin build-krun-init-blob -- \
  --target aarch64-unknown-linux-gnu \
  --root-qcow2 https://cloud-images.ubuntu.com/daily/server/releases/26.04/release-20260720/ubuntu-26.04-server-cloudimg-arm64.img

For x86_64 hosts use:

  • https://cloud-images.ubuntu.com/daily/server/releases/26.04/release-20260720/ubuntu-26.04-server-cloudimg-amd64.img
  • guest_target = "x86_64-unknown-linux-gnu"

Image metadata and cache helpers are exported by vmrunner:

  • EXTERNAL_QCOW_FILESYSTEMS: structured entries with name, arch, url, and digest
  • QCOW2_IMAGE_CACHE_DIR, qcow2_image_cache_path_with_sha256, ensure_qcow2_image_cached_with_sha256

Known URLs are cached under target/vmrunner-qcow2-imag/sha256/<digest>/. FreeBSD .qcow2.xz metadata remains exported for cache helpers, but FreeBSD guests are disabled for now.

Image smoke tests use a libtest-mimic custom harness and run through Cargo:

cargo test --test distro_vms -- --nocapture

Ubuntu and Fedora pass on the current x86_64 host. The FreeBSD trial is registered but ignored while FreeBSD guests are disabled.

Ubuntu cloud images normally put / on the first partition, so use /dev/vda1.

#[vmrunner::test(
    root_qcow2 = "https://cloud-images.ubuntu.com/daily/server/releases/26.04/release-20260720/ubuntu-26.04-server-cloudimg-arm64.img",
    guest_target = "aarch64-unknown-linux-gnu"
)]
async fn ubuntu_true(setup: vmrunner::TestSetup) -> anyhow::Result<()> {
    let node = vmrunner::Node::new(&[], "/usr/bin/true");
    let running = vmrunner::TestCase::new(&[&node])
        .root_qcow2(setup.root_qcow2_path())
        .guest_init(setup.guest_init_path())
        .root_device(vmrunner::RootDevice::virtio_first_partition())
        .launch()
        .await?;
    assert_eq!(running.wait().await?, vec![0]);
    Ok(())
}

How it works

  • #[vmrunner::test] requires root_qcow2; guest_target can be omitted, fixed, or host-mapped.
  • HTTP(S) root_qcow2 values are cached under target/vmrunner-qcow2-imag.
  • Missing init.krun sidecars are built before Linux namespace setup.
  • Linux tests always re-exec through unshare before VM launch.
  • TestCase creates a writable qcow2 overlay per VM node with imago.
  • libkrun starts init.krun, mounts the qcow2 root disk, then direct-execs the requested guest command.
  • krun-init-blob stays out of the normal dependency graph. Bump it in krun-init-build-package/Cargo.toml.

GPU support is opt-in with --features gpu.