Files
vm-images/README.md
T
guofangandClaude Sonnet 5.5 21604f511f Add a script that builds a fast Windows installation disk image
QEMU's emulated USB CD-ROM reads the Windows ISO at a few MB/s. The script
builds a bootable FAT32 GPT disk image from the ISO, splitting install.wim into
install*.swm parts for the 4 GB FAT32 file limit, so the image can be attached
as an NVMe disk and read about 15 times faster.

It runs on macOS and Debian. The ISO has to be read as UDF: hdiutil on macOS,
mount -t udf as root on Linux, otherwise udisksctl, otherwise 7z extracts it.
A desktop session automounts the loop device that udisksctl creates, so the
existing mount point is reused when there is one. The image itself is built
without root.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 17:28:18 +08:00

3.5 KiB

vm-images

Shared, locally cached virtual-machine installation media and base images. Downloaded ISO, QCOW2, IMG, VHD, and VHDX files are intentionally excluded from Git.

Download images

Download every supported image:

./download-images.sh

Download or check selected targets:

./download-images.sh debian13
./download-images.sh almalinux10
./download-images.sh fedora44
./download-images.sh ubuntu2604
./download-images.sh virtio-win

The Windows installation ISO is not downloaded because Microsoft's temporary download URLs expire. Place it manually in install-iso/; the script finds it automatically by matching Win, 11, the architecture (x64, or arm64 on arm64 hosts) and .iso in that order (*Win*11*x64*.iso or *Win*11*arm64*.iso, case-insensitive). If several files match, the highest version-sorted name is used. Check which file was picked with ./download-images.sh --print-path windows11 --iso.

Query image paths

The path interface prints one absolute path without downloading anything:

./download-images.sh --print-path debian13
./download-images.sh --print-path almalinux10
./download-images.sh --print-path fedora44
./download-images.sh --print-path ubuntu2604
./download-images.sh --print-path virtio-win
./download-images.sh --print-path windows11
./download-images.sh --print-path windows11 --iso

The Windows target prints the QCOW2 path by default. Add --iso to print its manually supplied installation ISO path. The QCOW2 is named after the detected ISO (for example Win11_25H2_English_x64_v2.iso → base-qcow2/Win11_25H2_English_x64_v2.qcow2); if no ISO is present, an existing *Win*11*x64*.qcow2 (*Win*11*arm64*.qcow2 on arm64) in base-qcow2/ is used instead.

Download URLs can be overridden with DEBIAN_IMAGE_URL, ALMA_IMAGE_URL, FEDORA_IMAGE_URL, UBUNTU_IMAGE_URL, and VIRTIO_WIN_ISO_URL. Each may list several mirrors of the same file, separated by whitespace; the Fedora image defaults to the TUNA, BFSU and NJU mirrors, and the Ubuntu image to the TUNA, NJU and official cloud-images.ubuntu.com mirrors.

When aria2 is installed (aria2c), an image is downloaded from all of its mirrors at once, with a single connection to each mirror. Without it, curl (or wget) tries the mirrors in order. A partial download is only resumed with the same downloader and mirror list; otherwise it starts over. The Fedora and Ubuntu checksums can be overridden with FEDORA_IMAGE_SHA256 and UBUNTU_IMAGE_SHA256 when using a custom image.

Windows installation disk image

QEMU's emulated USB CD-ROM reads the Windows ISO at only a few MB/s on macOS. build-windows-install-disk.sh builds a bootable FAT32 disk image from the ISO (install.wim is split into install.swm parts because of the 4 GB FAT32 file limit); attached as an NVMe disk it is read about 15 times faster.

# macOS (Homebrew), once
brew install wimlib dosfstools gptfdisk mtools
# Debian, once (p7zip-full or udisks2 only for non-root use)
sudo apt install wimtools dosfstools gdisk mtools p7zip-full

./build-windows-install-disk.sh [--force] install-iso/Windows11_..._arm64_....iso

The output defaults to the ISO path with .install.img in place of .iso. The image is built without root. The ISO has to be read as UDF (its ISO 9660 side only holds a README): macOS uses hdiutil, Linux uses mount -t udf as root, otherwise udisksctl, otherwise 7z extracts it into a temporary directory (which needs the free space of the whole ISO). The *.img file is ignored by Git.