How to use virtual machines

A workshop runs in an LXD container unless its definition says otherwise. Workshop can instead run it as a full virtual machine with a kernel of its own, which draws a harder boundary than a container can. Reach for a virtual machine when you want that boundary, or when the workshop itself has to run LXD containers and virtual machines. The boundary stops at the kernel: the project directory, including its .git directory, is still mounted read-write, and outbound network access is still open, so a virtual machine doesn’t protect the project from a coding agent that works in it.

Warning

The virtual machine runtime is experimental. Containers remain the default and the supported choice. A virtual machine consumes more memory and disk than a container, and it does not support every feature a container does; the Limitations section lists what it skips. A Workshop upgrade can also change the runtime incompatibly, so you may need to remove your virtual machine workshops before you upgrade. Opt in only when you need the stronger boundary.

Prerequisites

Before starting, ensure you have these requirements satisfied:

  • A Workshop installation that supports the runtime key.

  • LXD from the 6/candidate channel, on a host with hardware virtualization available.

  • A workshop you have not launched yet. The runtime is fixed when a workshop is launched, so an already running container workshop cannot be converted.

Check that Workshop lists the runtimes it supports:

$ workshop init --help

  ...
  The supported runtimes are lxd-container (the default) and lxd-vm.
  ...

Switch LXD to the 6/candidate channel:

$ sudo snap refresh --channel=6/candidate lxd
Warning

The candidate channel carries LXD builds that haven’t been released yet, so it is experimental as well. The switch applies to the whole host, including your container workshops, and LXD doesn’t support moving back from 6/candidate to 6/stable.

Opt in to virtual machines

Until you opt in, Workshop refuses to launch a virtual machine workshop and reports the two commands that change that:

$ workshop launch <NAME>

  error: cannot launch "<NAME>": runtime "lxd-vm" is experimental
  To opt in: "sudo snap set workshop workshop.experimental-vms=1 && sudo snap restart workshop.workshopd"

Run both of them:

$ sudo snap set workshop workshop.experimental-vms=1
$ sudo snap restart workshop.workshopd

  Restarted.

Create a virtual machine workshop

Generate a definition with the --runtime flag:

$ workshop init <NAME> --runtime lxd-vm

  "<NAME>" workshop definition created at /home/user/my-project/.workshop/<NAME>.yaml

The generated definition carries the runtime:

.workshop/<NAME>.yaml
name: <NAME>
base: ubuntu@24.04
runtime: lxd-vm

Add the same key by hand to a definition you already wrote. runtime accepts lxd-container and lxd-vm; omitting it selects lxd-container.

Launch and verify

Launch the workshop the same way as any other:

$ workshop launch <NAME>

  "<NAME>" launched

A virtual machine boots from a different base image than a container, so the first launch on a given base downloads that image.

Confirm what you got:

$ workshop info <NAME>

  name:      <NAME>
  base:      ubuntu@24.04
  project:   ~/my-project
  hostname:  <NAME>.my-project.wp
  status:    ready
  runtime:   lxd-vm
  notes:     --

The runtime line is the confirmation: a container workshop reports runtime: lxd-container instead.

Manage the workshop

Stopping, starting, and removing a virtual machine workshop use the same commands as a container:

$ workshop stop <NAME>

  "<NAME>" stopped
$ workshop start <NAME>

  "<NAME>" started
$ workshop remove <NAME>

  "<NAME>" removed

A running virtual machine workshop also starts again on its own after the host reboots, and it is reachable over SSH by hostname, in both cases exactly as a container workshop is. See Cross-workshop networking for reaching a workshop by hostname.

Limitations

Virtual machine workshops are not at parity with containers. The differences below change what you can do with one.

The runtime is fixed at launch

Editing runtime in the definition of a launched workshop and refreshing it fails:

$ workshop refresh <NAME>

  error: cannot refresh "<NAME>": cannot refresh "<NAME>": runtime changed from "lxd-vm" to "lxd-container"

Remove the workshop and launch it again to change the runtime.

SDK support depends on LXD

On LXD from the 6/stable channel, a virtual machine workshop that declares an SDK is refused:

$ workshop refresh <NAME>

  error: cannot refresh "<NAME>": cannot refresh "<NAME>": SDKs are currently unavailable for virtual machines

Switch LXD to 6/candidate as the prerequisites require.

Most interfaces are unsupported

Currently, virtual machine workshops only support the mount and secret interfaces. Other interfaces will not be auto-connected and cannot be connected manually.

Virtual machine workshops can still install SDKs that contain unsupported plugs and slots, but these plugs and slots will not be listed in workshop connections.

Removing stopped workshops

Disconnecting a mount interface plug requires the workshop to be running. This can prevent removing a stopped workshop. Either start the workshop before removing it, or see How to purge malfunctioning workshops. A future Workshop release will remove this limitation.

Virtual hardware is restrictive

Virtual machines are modeled on physical hardware, and have some of the same limitations. When the workshop starts, the following quantities are set in stone:

  • The number of processors.

  • Total system memory.

  • The root filesystem size.

Interfaces can be connected and disconnected while the workshop is running, but much like devices plugged into a physical machine, there’s a limit to the total number of interface connections.

The simplest way to avoid these limits is to disconnect unused interfaces. In some cases, Workshop uses the number of SDKs to predict hardware requirements; adding an SDK without many plugs or slots can help to compensate for one with several active connections.

See also

Explanation:

How-to guides:

Reference: