Commands · reference

vmlab vm

10 min read · 2026-09-02 · vmlab 0.9

vmlab vm controls one VM at a time: power, its IP address, and the display-driven interaction described in Screens, input and vision, which the wscript Machine handle offers to scripts and this verb offers to a shell.

sh
vmlab vm <COMMAND>
SubcommandMeaning
startStart one VM.
ipPrint a VM's IP address (defaults to the first NIC with a lease).
stopStop one VM (graceful ladder; --force to kill).
restartRestart one VM.
destroyDestroy one VM: stop it and delete its clone (config retained).
screenshotCapture a running VM's screen to a PNG file.
sendkeysSend a key chord (e.g. ctrl-alt-delete).
mouse-moveMove the mouse pointer to absolute screen coordinates.
clickClick a mouse button, optionally first moving to x,y.
dragPress, drag from x1,y1 to x2,y2, and release the left button.
ocrOCR the screen (optionally a region).
find-imageSearch the screen for a template image.
-h, --helpPrint help.

Every subcommand takes a VM reference of the form [lab/]vm. A bare name is resolved against the lab in the current directory, and the lab daemon is started if none is running. The qualified form addresses a lab that is already running from any directory and never starts one; it fails with lab "<name>" is not running otherwise. A malformed reference such as lab/ is refused before anything is contacted.

The power subcommands and ip accept a container name too and behave the same; vmlab container is the same request under the other noun. The display subcommands need a framebuffer, which a container never has, so on a container they fail with unsupported.

vmlab vm start

sh
vmlab vm start <VM>
OptionMeaning
<VM>The VM, as [lab/]name.
-h, --helpPrint help.

Starts one VM without running the lab's up plan: no dependency waves, no provision scripts, no playbooks. Any deferred download the VM needs runs first with progress on the terminal, then the host binaries are checked, then the VM boots. A VM whose clone does not exist yet gets one created from its template, and its first-boot script runs before it reports ready, exactly as under vmlab up. A VM that is already running is left alone. A suspended VM, one whose guest put itself to sleep, is woken with QMP system_wakeup instead: no second QEMU, no agent refresh, and the verb returns once the guest runs again, emitting vm.woken with cause = start. Nothing is printed on success.

The VM is built from the lab file as it is on disk now. If vmlab.wcl has changed since the lab daemon loaded it, the verb replaces the daemon first, as vmlab up does, and refuses when another machine in the lab is still running. A VM whose clone was made from a different template than the one the file now names refuses to boot; see Editing the lab file between runs.

vmlab vm ip

sh
vmlab vm ip [OPTIONS] <VM>
OptionMeaning
<VM>The VM, as [lab/]name.
--nic <NIC>Report this NIC's address instead of the first one.
-h, --helpPrint help.

Asks the guest agent for its interfaces, matches them to the VM's NICs by MAC address and prints one IPv4 address followed by a newline. With no --nic it is the first NIC that holds an address, in declaration order. --nic takes a zero-based index into the VM's nic {} blocks and reports that NIC's address, failing with `no IPv4 address reported by agent` if that NIC has none. The VM must be running with its agent answering; a VM still booting fails the same way.

vmlab vm stop

sh
vmlab vm stop [OPTIONS] <VM>
OptionMeaning
<VM>The VM, as [lab/]name.
--forceHard kill instead of the graceful ladder.
-h, --helpPrint help.

Stops one VM through the graceful ladder: a shutdown through the guest agent, then an ACPI power-down, then a hard kill, each with a timeout. See vmlab down for the rungs and their timeouts. --force kills QEMU at once. Unlike down, this stops only the named VM; machines that depend on it keep running. A VM that is already stopped is a no-op. Nothing is printed on success.

vmlab vm restart

sh
vmlab vm restart <VM>
OptionMeaning
<VM>The VM, as [lab/]name.
-h, --helpPrint help.

Stops the VM through the graceful ladder, waits up to 60 seconds for it to settle as stopped, and boots it again. The clone is kept, so the guest comes back with its disk state and no first-boot script. Port forwards that target the VM are re-installed by the daemon when it gets a new lease. A VM that does not settle in time fails with `<name> did not stop for restart`.

Restart applies the lab file the same way vm start does. A running VM is a machine a new daemon cannot take over, so restarting it after an edit to vmlab.wcl refuses; run vmlab down and then vmlab up (or vm start) to boot it with the edit.

vmlab vm destroy

sh
vmlab vm destroy <VM>
OptionMeaning
<VM>The VM, as [lab/]name.
-h, --helpPrint help.

The per-machine form of vmlab destroy. The workspace syncer for the VM is stopped, the VM is force-stopped and its clone, runtime directory, snapshots, workspace sync ledger and any agent-repair divergence are deleted. The VM stays declared in the lab file, so a later vmlab up <vm> re-creates it from the template and runs its first-boot script again. The rest of the lab keeps running. Prints vm "<name>" destroyed.

Snapshots go with the clone

A VM's snapshots live inside its qcow2 clone. vmlab vm destroy deletes them along with the disk, and nothing restores them.

vmlab vm screenshot

sh
vmlab vm screenshot <VM> <PATH>
OptionMeaning
<VM>The VM, as [lab/]name.
<PATH>Output PNG path.
-h, --helpPrint help.

Captures the VM's framebuffer to a PNG. A relative path is made absolute against your current directory before it is sent, because the daemon writes the file and has a working directory of its own. The path written is printed on success. The VM must be running.

vmlab vm sendkeys

sh
vmlab vm sendkeys <VM> <CHORD>
OptionMeaning
<VM>The VM, as [lab/]name.
<CHORD>The key chord, e.g. ctrl-alt-delete.
-h, --helpPrint help.

Presses and releases one chord on the VM's virtual keyboard, in the same spelling the wscript send_keys call accepts: key names joined with -. To type a string rather than a chord use a script and the type call described in Screens, input and vision. Nothing is printed on success.

vmlab vm mouse-move

sh
vmlab vm mouse-move <VM> <X> <Y>
OptionMeaning
<VM>The VM, as [lab/]name.
<X>, <Y>Absolute screen coordinates, in pixels from the top-left corner.
-h, --helpPrint help.

Moves the pointer to an absolute position on the guest screen without clicking. Nothing is printed on success.

vmlab vm click

sh
vmlab vm click [OPTIONS] <VM> [X] [Y]
OptionMeaning
<VM>The VM, as [lab/]name.
[X], [Y]Move here before clicking. Omit both to click at the current position.
--button <BUTTON>Button to click: left, right or middle. Default left.
-h, --helpPrint help.

Clicks one button, moving the pointer first when coordinates are given. Giving only one of X and Y is refused with `click coordinates need both x and y` before anything is contacted. Nothing is printed on success.

vmlab vm drag

sh
vmlab vm drag <VM> <X1> <Y1> <X2> <Y2>
OptionMeaning
<VM>The VM, as [lab/]name.
<X1>, <Y1>Where the left button is pressed.
<X2>, <Y2>Where it is released.
-h, --helpPrint help.

Presses the left button at the first point, moves to the second and releases. Nothing is printed on success.

vmlab vm ocr

sh
vmlab vm ocr [OPTIONS] <VM>
OptionMeaning
<VM>The VM, as [lab/]name.
--region <X> <Y> <W> <H>Restrict to a region: x y w h.
-h, --helpPrint help.

Runs OCR over the current screen, or over the rectangle --region names, and prints the recognised text followed by a newline. The four region values are separate arguments; negative values are clamped to 0. The recogniser and its limits are described in Screens, input and vision.

vmlab vm find-image

sh
vmlab vm find-image [OPTIONS] <VM> <IMAGE>
OptionMeaning
<VM>The VM, as [lab/]name.
<IMAGE>Template image path (PNG/PPM).
--threshold <THRESHOLD>Match threshold 0.0–1.0. Default 0.9.
--region <X> <Y> <W> <H>Restrict the search to a region: x y w h.
-h, --helpPrint help.

Searches the screen for the template image and prints the best match as one line, x=.. y=.. w=.. h=.. score=.. center=x,y. The image path is resolved on the host before the request is sent, so a missing file is refused with reference image <path> and no daemon is contacted. When nothing scores at or above the threshold the verb prints no match on stderr and exits 1, which makes it usable as a condition in a shell loop.

Examples

Restart a VM and wait for it to get an address:

sh
vmlab vm restart client01
until vmlab vm ip client01 2>/dev/null; do sleep 5; done

Log in on a Windows console from the shell:

sh
vmlab vm sendkeys client01 ctrl-alt-delete
vmlab vm screenshot client01 /tmp/login.png
vmlab vm ocr client01 --region 0 400 1024 200

Wait for a button to appear, then click it:

sh
until out=$(vmlab vm find-image client01 ./ok-button.png); do sleep 2; done
center=${out##*center=}
vmlab vm click client01 ${center%,*} ${center#*,}

Exit status

Exit status is 0 on success. Exit 4 (not_found) means the lab declares no VM by that name, for stop, ip and every display subcommand. Exit 6 (unsupported) means the machine has no display, which is every container. start, restart and destroy exit 1 (failed) on an unknown name and on any boot, stop or disk failure; the other subcommands exit 1 on a VM that is not running, an agent that does not answer, or a local problem such as an unreadable image or an unreachable lab. find-image exits 1 on no match. Exit 5 (conflict) means the supervisor tracks a lab with this name from another directory. A usage error, including a bad --button value, exits 2.