.qcow2 file), and SmolVM handles the firmware, virtual TPM, QEMU wiring, and SSH plumbing for you.
You can boot the VM, run PowerShell commands inside it with vm.run(...), and upload files to Windows-style paths with vm.upload_file(...) — the same API you use for Linux guests.
This guide explains when to use Windows guests, what you need before you start, and how to drive one end to end.
Windows guest support is rolling out in phases. Boot, PowerShell command execution, file upload, environment variable injection, and unattended image building work today. Host mounts, network controls, and snapshots are not yet supported — see Current limitations.
What this is
A Windows sandbox is a virtual machine that runs the real Windows 11 operating system instead of Linux. You’d reach for it when:- You’re testing software that only ships for Windows.
- You’re building an agent that needs to drive a Windows desktop or Windows-only application.
- You want a disposable Windows environment that won’t touch your host machine.
SmolVM(...) in Python — and is isolated from your host using KVM hardware virtualization.
Before you start
You need three things on the host:- A Linux host with KVM. Windows guests use QEMU + KVM. macOS hosts are not supported yet.
- A Windows 11
qcow2disk image with OpenSSH Server. The easiest way to get one is thesmolvm windows build-imageCLI command, which drives an unattended Windows install from a stock Windows ISO + the virtio-win driver ISO and produces a ready-to-useqcow2(OpenSSH, virtio-win drivers, and a known local admin account all pre-installed). If you already have your own Windowsqcow2, point SmolVM at that file instead — just make sure the OpenSSH Server feature is installed and running, otherwisevm.run(...)andvm.upload_file(...)will fail with a connection or auth error. - OVMF (UEFI firmware) and
swtpm(software TPM). Windows 11 requires UEFI Secure Boot and TPM 2.0. SmolVM looks for OVMF in the standard distro install paths and will tell you exactly which package to install if it’s missing.
- Debian / Ubuntu
- Fedora
- Arch
Build a Windows image
If you don’t have a Windowsqcow2 yet, smolvm windows build-image produces one for you in a single command. You provide a Windows ISO and the virtio-win driver ISO, walk away for 15–30 minutes, and get back a ready-to-boot image:
smolvm / smolvm by default — override with --username and --password). Boot it like any other Windows image:
smolvm windows build-image reference for all available flags (edition, hostname, disk size, build timeout) and for using WindowsImageBuilder directly from Python.
Launch a Windows sandbox
PointSmolVM at your Windows qcow2 file, pass os="windows", and supply the Windows account credentials that map to the OpenSSH user inside the image:
- SmolVM copies the OVMF UEFI variable store to a per-VM location so the firmware can persist boot order independently.
- It starts a per-VM
swtpmsoftware TPM 2.0 process. - It launches QEMU with the right machine type (
q35), Hyper-V enlightenments for performance, and avirtio-scsiroot disk. - On
stop()ordelete(), theswtpmsidecar and per-VM firmware state are cleaned up.
Accepted image paths
image= accepts any of these for Windows guests:
- An absolute path:
image="/var/lib/vms/win11.qcow2" - A tilde-relative path:
image="~/win11-vm/disk.qcow2" - A
file://URI:image="file:///var/lib/vms/win11.qcow2"
SSH user and password
Windows OpenSSH does not support theroot user that SmolVM uses for Linux guests. Pass the local Windows account you set up inside the qcow2:
ssh_user="..."— the Windows account name (for example"Administrator", or whatever local user you created).ssh_password="..."— that account’s password.
ssh_password is set, SmolVM uses paramiko’s password-auth path and does not try SSH keys. If you have configured key-based SSH inside the Windows image, drop ssh_password and pass ssh_key_path= instead.
Run PowerShell commands
vm.run(...) works on Windows guests just like on Linux guests, but the command string is interpreted by PowerShell instead of sh:
powershell.exe -NoProfile -EncodedCommand so quotes, backticks, and special characters survive the Windows OpenSSH cmd.exe layer unchanged. You don’t need to escape anything yourself.
Inject environment variables
Pass secrets and configuration into a Windows sandbox the same way you do for Linux — viaenv_vars= on the constructor, or vm.set_env_vars(...) / vm.unset_env_vars(...) / vm.list_env_vars(...) at runtime:
HKCU\Environment (the standard per-user environment store) via [Environment]::SetEnvironmentVariable(name, value, 'User'). SmolVM tracks which variables it owns through a sentinel key, so list_env_vars and unset_env_vars only ever touch values SmolVM set — anything you configure inside Windows yourself is left alone.
Variables become visible to new processes, not the SSH session that set them. Every vm.run() call opens a fresh SSH session, so the very next vm.run() after set_env_vars will see the new values. See the environment variables guide for the full API.
Upload files into Windows paths
vm.upload_file(...) accepts Windows-style destination paths. All three forms work:
- Native Windows:
"C:\\Users\\celesto\\hello.ps1" - Forward-slash mix:
"C:/Users/celesto/hello.ps1" - SFTP/POSIX style:
"/C:/Users/celesto/hello.ps1"
New-Item -ItemType Directory -Force, so you can upload straight into a path that doesn’t exist yet.
Memory defaults
Windows 11 defaults to 4096 MiB of RAM in SmolVM. The minimum that boots is 2 GiB, but Edge and Defender want more. Override withmemory=:
Your baseline image stays read-only
When you pointSmolVM at a Windows qcow2, SmolVM does not write to that file. Each sandbox gets its own thin scratch disk — a per-VM qcow2 overlay stacked on top of your baseline — so the original image stays byte-for-byte unchanged across runs.
This gives you two things for free:
- Run multiple Windows sandboxes from the same image in parallel. Each
SmolVM(os="windows", image=...)call gets a fresh overlay, so concurrent processes don’t fight over the disk write lock. - Crashes and Ctrl-C don’t corrupt your baseline. If a sandbox dies mid-run, the only thing lost is its overlay. Your golden image is untouched.
qemu-img create -b and lives under data_dir/disks/{vm_id}.qcow2. It is deleted automatically when the VM is deleted unless you set retain_disk_on_delete=True on the VMConfig.
VMConfig directly with disk_mode="shared". See VMConfig for the full options.
How os= and image= work together
SmolVM uses two slightly different rules depending on where the image comes from:
| Image source | Pass os=? | Why |
|---|---|---|
Local file (path or file://) | Yes | The file alone doesn’t tell SmolVM which OS is inside. |
| Published S3 image | No | The image manifest already records the OS — passing os= errors. |
os="windows".
Current limitations
The first release of Windows guest support is deliberately narrow. The following raise a clear error rather than silently misbehaving:- Linux hosts only. macOS support comes in a later phase.
- No
mounts=. Host directory mounts (virtio-9p) are Linux-guest only for now. - No
internet_settings=. Domain allowlists and egress controls are not yet wired into the Windows network stack. - No snapshots.
vm.snapshot()andSmolVM.from_snapshot()are rejected for Windows VMs — Windows guests use multiple state artifacts (qcow2 + UEFI vars + TPM state) that can’t be checkpointed atomically yet.
ValueError with a plain-English message naming the feature and the workaround.
Troubleshooting
OVMF not found
OVMF not found
SmolVM probes the four standard install paths (Debian, Fedora, Arch, Homebrew). If none match, it raises a
ValueError naming the package to install. Re-run after installing the ovmf / edk2-ovmf package from the table above.`swtpm` not found
`swtpm` not found
Install the
swtpm package from your distro. The error message includes the install hint.Local image not found
Local image not found
image= must point at an existing .qcow2 file. Tilde and relative paths are expanded against your current working directory.Windows guests need a pre-installed disk image
Windows guests need a pre-installed disk image
You passed
os="windows" without image=. SmolVM does not build a Windows image for you yet — point at a qcow2 you’ve already installed Windows into.`vm.run()` or `vm.wait_for_ssh()` fails with an auth or connection error
`vm.run()` or `vm.wait_for_ssh()` fails with an auth or connection error
SmolVM expects the OpenSSH Server feature to be installed and running inside the Windows image, and the
ssh_user / ssh_password you passed must match a real local Windows account. Install OpenSSH Server (Settings → Apps → Optional features → “OpenSSH Server”), start the sshd service, and confirm you can SSH in from the host before retrying.Next steps
Build a Windows image
Run
smolvm windows build-image to produce a Windows qcow2 unattendedBackends
Why Windows guests run on QEMU
VMConfig
The full configuration model, including
guest_osVM lifecycle
Start, stop, and delete sandboxes
