Project

susi-qemu

0.0
No release in over a year
Config, manage, and maintain multiple QEMU instances for your projects
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 1.1.1
~> 1.3.0
~> 7.2.0
 Project Readme

Susi

A small Ruby CLI for disposable project VMs, with optional browser management and VNC.

ARM64 Linux guests run with Apple Hypervisor.framework (HVF) acceleration on Apple Silicon macOS. Native x86_64 guests use KVM on x86_64 Linux. Other host/guest combinations use QEMU TCG emulation. Windows guests always use x86_64, including on Apple Silicon.

Host ARM64 Linux x86_64 Linux x86_64 Windows 10/11
Apple Silicon macOS HVF TCG TCG
x86_64 Linux TCG KVM KVM

The core uses Ruby standard libraries and external QEMU/OpenSSH tools. WEBrick is optional; noVNC is bundled. No libvirt, database, Node, or always-running manager is required.

Install

Requirements: Ruby 3.3+, QEMU 9.2+, qemu-img, OpenSSH client, and UEFI firmware. Linux also needs xorriso; macOS uses its built-in hdiutil.

On Apple Silicon:

brew install ruby qemu
export PATH="/opt/homebrew/opt/ruby/bin:$PATH"
gem build susi.gemspec
gem install ./susi-2.0.0.gem
export PATH="$(ruby -e 'puts Gem.bindir'):$PATH"
susi doctor

On Linux, install a supported Ruby and QEMU from your distribution, plus the QEMU ARM/x86 system packages, OVMF, AAVMF, openssh-client, and xorriso. Enable virtualization and grant your user access to /dev/kvm. susi doctor --json reports missing tools, accelerators, and firmware. QEMU versions older than 9.2 are rejected.

For source checkout use ruby bin/susi in place of susi. The macOS system Ruby 2.6 is unsupported.

First Linux project

# On Apple Silicon, native means aarch64 and the VM uses HVF.
susi template create debian-dev --arch native
susi template ssh debian-dev
# Inside: install your tools, optionally a desktop, then exit SSH.
susi template publish debian-dev --default

cd /path/to/project
susi init
susi start --wait
susi ssh
# Inside the guest, the project is at /workspace.
susi stop
susi start --wait             # Preserves VM disk changes
susi delete                  # Requires stopped VM; preserves project files
susi start --wait             # A clean linked clone of the template

The first template command downloads a dated Debian 13 generic cloud image and verifies its embedded SHA512 digest. Allow cloud-init to finish before customization or publication. Publishing waits for cloud-init, checks 9p support, removes machine identity and builder SSH keys, stops the VM, and seals a standalone read-only qcow2 revision.

To prepare the other architecture, pass --arch x86_64 or --arch aarch64. Template aliases are resolved by architecture, so debian-dev can have both revisions. To import a compatible standalone cloud-init image:

susi template create custom --image /path/to/cloud.qcow2 --arch aarch64

susi template list includes preparation VMs and published revisions. A published revision in use cannot be deleted. New projects resolve the latest matching revision; existing instances keep their pinned revision. There is no command to copy a project VM.

Choose or change the default from any already published template:

susi template default debian-dev
susi template default             # Show the current default

In the web interface, open Templates and click Make default. The selected template is marked Default, and the new-project form shows its name. Leave the template field empty to use it; the guest system and graphical-console setting inherit from the template unless overridden.

The default is saved per Susi user on the VM host and applies to both the CLI and web interface. susi init without --template records the selected default in susi.yml; an explicit template takes precedence. A hand-written susi.yml may also omit template, in which case VM creation uses the current default. Changing the default does not retarget existing VMs. If such a VM is deleted and recreated while its YAML still omits template, it uses the new default. The default names a template family: new VMs use its latest published revision for their architecture. A missing compatible revision produces an error instead of silently selecting another template. Deleting the last published revision of the default clears the selection.

Project configuration

susi init creates susi.yml. Commands search parent directories for it.

The VM's display name is the project directory name: /projects/my-app creates a VM named my-app. New project VMs also use that name for the guest hostname. The display name preserves the directory's spelling; guest hostnames use lowercase ASCII letters, digits and hyphens, with spaces and other characters replaced by hyphens. Susi limits Linux hostnames to 63 characters and Windows names to 15, uses vm if normalization leaves no usable name, and prefixes numeric-only Windows names with vm-. Existing VMs retain their hostname when upgraded or restarted; this naming applies to newly created instances. Template preparation VMs keep their generated hostnames.

version: 1
template: debian-dev
arch: native
cpus: 2
memory: 2GiB
disk: 30GiB
gui: false

Only version and template are required. arch: native selects aarch64 on Apple Silicon and x86_64 on x86 Linux. Explicit aarch64/x86_64 are supported, with arm64/amd64 aliases. Cross-architecture VMs use TCG; native acceleration failures are errors. To deliberately use emulation, set accel: tcg or run susi start --accel tcg.

Change CPU, memory, GUI, or sharing settings while stopped, then start again. Disks can grow but cannot shrink; expanding a partition/filesystem inside an existing guest may require guest administration. Template or architecture changes require deleting and recreating the instance. Both headless and graphical ARM64 guests retain HVF acceleration.

Linux project mounts use QEMU 9p. Source files are live in both directions. Use polling for development watchers that do not observe host-originated changes, and keep databases or filesystem-intensive caches on the guest disk. Deleting a VM does not undo writes it made to the shared project.

start returns when QEMU is running. start --wait waits for SSH and the project mount; cross-architecture guests can take much longer. A readiness timeout leaves the VM running for diagnosis. stop requests a guest shutdown and waits 120 seconds; stop --force explicitly powers it off.

GUI and browser management

Set gui: true and use a template with a desktop installed. Susi supplies a VNC display; it does not install a desktop automatically or provide 3D acceleration. The browser console fits the complete guest screen to the available window by default without changing the guest resolution. Use Fullscreen for more space, or uncheck Fit to window to use the original size with scrolling.

susi vnc                     # Prints native VNC endpoint and password
gem install webrick --version '~> 1.9.2'
susi web                     # Prints an authenticated localhost URL
susi web --open              # Also opens your browser

The browser lists project VMs and templates, runs lifecycle operations, shows logs, and opens graphical consoles using bundled noVNC. Long operations run independently of the web process. Closing web management leaves VMs running. HTTP, forwarded guest SSH, native VNC, and QEMU WebSocket listeners bind to loopback; QMP uses private Unix sockets.

For a remote QEMU host:

susi web --ssh user@server --open
# If local port 8765 is occupied:
susi web --ssh user@server --port 8766 --remote-port 8765

Install Susi on both machines and make susi available in the remote noninteractive SSH PATH. Only the VM host needs QEMU. This starts the server on the remote machine and carries the interface and every browser VNC console over one SSH tunnel. Your usual OpenSSH configuration, host-key checks, and ProxyJump settings apply. Projects live on the remote VM host, not on the browsing laptop. No direct LAN HTTP access is supported.

Native VNC can also use a dedicated tunnel with susi vnc VM_ID --ssh user@server.

USB devices

Susi can register a physical USB device on the VM host for a project or a template preparation VM. Project assignments are saved in susi.yml, so you can configure them immediately after susi init, before creating or starting a VM. Start the command-line wizard with:

susi usb add                 # Detects newly plugged devices for this project
susi usb add --project PATH  # Configure an initialized project from elsewhere
susi usb add VM_ID           # Add newly plugged devices for a specific VM
susi usb add template:NAME   # Add devices for a template preparation VM
susi usb list                # Show this project's registrations and connection state

The wizard records the connected USB devices before asking you to plug in the devices you want to add. Plug them into the VM host and press Enter. Susi compares the new inventory with that baseline, shows every newly connected device, and asks for one confirmation to save all of them together. Devices connected before the prompt are excluded. If nothing new appears, press Enter to try again. If your devices were already connected, unplug them and enter r to reset the baseline, then plug them in again. Enter q or send end-of-input to cancel without changing the configuration.

USB setup does not create or start a VM. Run susi init first if the project has no susi.yml; outside an initialized project, the wizard can also ask you to choose an existing VM. If any detected device is missing or belongs to another project or VM, Susi rejects the whole group before saving; resolve the conflict and retry. In the web interface, use Project USB devices and enter the initialized project's directory, or use an existing VM's USB devices action. Wait for the insertion prompt, plug in the devices, select Detect devices, then Review all detected devices to confirm the group. Start detection again resets the baseline after you unplug the desired devices. When using susi web --ssh, these are USB devices connected to the remote VM host.

A running VM receives the device immediately. A stopped or future VM receives it when started. Headless VMs already running before this feature was installed need one stop/start to gain a USB controller. Project registrations persist across VM restarts and deletion/recreation because they remain in susi.yml. Removing a registration updates the project configuration and detaches the device from a running guest. Template preparation VMs keep their assignments in their own metadata. Unmount USB storage before attaching it; the guest takes control of the selected hardware and the host can no longer use it while attached. Before removing storage, safely eject it inside the guest.

The wizard writes entries to the project's usb array in this form:

usb:
  - id: usb-f0c4739bae78ebb25a3fd5a5
    name: USB adapter
    vendor_id: "0403"
    product_id: "6010"
    serial: ADAPTER-1
    bus: 1
    port: "2.2"

Keep the generated identity fields together. A device without a serial number omits serial and uses its host bus and physical port as its identity. Hand-written entries can omit id and name; Susi derives them from the device's identity. Manual configuration edits apply at the next VM start. Set usb: [] to clear configured assignments for that next start; use susi usb remove to detach a device immediately from a running guest.

For scripts, copy a device's id from the host inventory:

susi usb devices --json
susi usb add --device DEVICE_ID --json  # Works before this project's VM exists
susi usb add VM_ID --device DEVICE_ID --json
susi usb list VM_ID --json
susi usb remove VM_ID --device DEVICE_ID --json

Supplying --device explicitly registers it without a confirmation prompt. With the wizard, --yes skips the final confirmation. --json for usb add requires --device and a VM ID, --project PATH, or the current initialized project, so its output stays machine-readable. usb list and usb remove also accept --project PATH and work before a VM exists; remove uses the registration id shown by usb list and works when the hardware is unplugged.

Susi matches a device by vendor/product IDs and serial number when available. Devices without a serial number must return to the same physical host bus and port. Identical devices with duplicate serial numbers cannot be distinguished safely. If registered hardware is missing, VM startup reports the missing device; reconnect it or remove its registration before starting. After unplugging and reconnecting a device while the VM runs, retry usb add --device DEVICE_ID or restart the VM to resolve its new host address. If attaching to a running VM fails, the error reports that the registration remains saved: fix the host issue and retry the add command, or remove the registration.

USB passthrough requires a QEMU build with usb-host support through libusb. Linux hosts need permission to read and write the selected /dev/bus/usb device node, usually configured with a device-specific udev rule. On macOS, a host driver holding the device can prevent QEMU from claiming it. A guest may also need its device's own driver. USB inventory, registration, and QMP behavior have automated coverage; physical USB hardware and macOS passthrough still require verification on their respective hosts.

If Linux reports that a USB device is not accessible, susi start, susi usb list, and the web USB panel show its device-node path and a targeted udev rule. An administrator must save that rule in the indicated /etc/udev/rules.d/99-susi-usb-….rules file on the VM host, run sudo udevadm control --reload-rules, and unplug/reconnect the device before retrying. The rule grants read/write access to the user ID running Susi and matches the device's vendor/product IDs plus its serial number, or its physical bus/port when a serial cannot be matched literally. Keep port-bound devices in the same host port. This also works for SSH/headless sessions and persists across reconnections; run Susi as your normal user. Registrations can still be saved before permissions are configured. A missing /dev/bus/usb node is reported separately, since a container must have access to the host device as well as the sysfs inventory.

Windows 10 Pro from an ISO

Supply a Windows 10 Pro x64 ISO, preferably 22H2. One command creates and starts the installation VM:

susi template create win10 --iso /path/to/Windows10.iso
susi web --open

In the browser, open Templates → win10 → Console. Press Enter if the ISO asks to boot from CD/DVD. If Setup asks for a product key, enter your key or choose I don't have a product key and activate later. Susi supplies the remaining setup answers: Pro edition, virtual-disk partitioning, Audit Mode, and its SSH preparation script. Keep the VM running while Windows installs and OpenSSH downloads. No manual PowerShell command is needed. The browser's Prepare template → Windows 10 Pro form supports the same workflow with just a name and ISO path on the VM host.

Windows 10 uses the UEFI firmware shipped with QEMU; on Linux install OVMF if needed. It does not require swtpm or manually supplied Secure Boot firmware. On Apple Silicon it runs through x86 emulation, which is substantially slower than native Linux guests.

Check preparation with susi template status win10. Once SSH is ready, customize and publish:

susi template ssh win10
# Customize Windows, then exit the SSH session.
susi template publish win10

The default setup language is en-US; use --locale zh-CN, for example, with matching installation media. --image-name "Windows 10 Pro" selects the exact edition name in the ISO. Use --manual-install for interactive installation. Windows activation is handled inside the guest. See Windows preparation for prerequisites, troubleshooting, and SMB project sharing.

For Windows 11, use --os windows11 and supply Secure Boot firmware with enrolled Microsoft keys plus swtpm. The existing --os windows command keeps its Windows 11 behavior. Windows templates must be prepared using Susi's scripts before publication. Each clone receives its own identity, SSH host keys, NVRAM, and (Windows 11) TPM state. Encrypted Windows template cloning is unsupported.

Use susi credentials to display a project VM's desktop username/password, or susi credentials --template win10 for its preparation VM. Credentials are not shown in status or logs. The automated preparation desktop uses Windows Audit Mode; keep it in Audit Mode until publication.

State and diagnostics

Commands accept an instance ID or --project /path/to/project. Use --json for machine-readable output.

susi list
susi status --json
susi logs
susi ssh -- uname -m
susi doctor --json

State defaults to $XDG_DATA_HOME/susi (or ~/.local/share/susi); settings and SMB credentials use $XDG_CONFIG_HOME/susi (or ~/.config/susi). Private short-lived QMP sockets use /tmp/susi-UID/BOOT_UUID to stay within macOS Unix-socket path limits. Set SUSI_HOME=/some/private/directory to isolate persistent state. Metadata and credentials use owner-only permissions.

The manager uses per-VM locks, atomic metadata writes, and QMP identity checks. It refuses conflicting operations and never terminates a process merely because a stale record contains the same PID. Stop VMs before moving or removing state directories. If a template download/start fails, inspect its preparation record and logs, fix the dependency, and restart or delete the draft.

Development and validation

bundle install
bundle exec rake
# Opt-in real QEMU launch/VNC smoke test on a compatible host:
SUSI_QEMU_SMOKE=1 bundle exec rake
# Full guest lifecycle using an already published Linux template:
bundle exec ruby test/manual_linux_smoke.rb debian-dev aarch64

The suite covers configuration, state locking, QMP event handling, machine profiles, lifecycle, seed generation, and web authorization/relay behavior. See validation notes for the actual machines and guest workflows exercised in this checkout. Linux KVM and Windows installation require their respective host/media for end-to-end verification.