> For the complete documentation index, see [llms.txt](https://docs.layeronecloud.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.layeronecloud.com/pve-microvm/architecture.md).

# Architecture

Source project: `pve-microvm` (migrated from `docs/architecture.md`). Run commands from the code checkout and directory specified below, not from this documentation repository.

## How it works <a href="#how-it-works" id="how-it-works"></a>

```
qm create 900 --machine microvm ...
         │
         ▼
PVE::QemuServer::Machine (Machine.pm) [PATCHED]
  ─ regex accepts 'microvm'
  ─ machine_base_type() → 'microvm'
  ─ get_vm_machine() → 'microvm' (no +pve0 suffix)
         │
         ▼
PVE::QemuServer (QemuServer.pm) [PATCHED]
  ─ config_to_command() detects microvm
  ─ delegates to MicroVM module
         │
         ▼
PVE::QemuServer::MicroVM (MicroVM.pm) [NEW]
  ─ validates config
  ─ generates QEMU command with PCIe devices
         │
         ▼
/usr/bin/qemu-system-x86_64
  -M microvm,...,pcie=on,acpi=on
  -kernel vmlinuz -initrd initrd
  -device virtio-blk-pci-non-transitional
  -device virtio-net-pci-non-transitional
  -device virtio-serial-pci-non-transitional
  -device virtio-balloon-pci-non-transitional
  -device vhost-vsock-pci-non-transitional
```

## Boot flow <a href="#boot-flow" id="boot-flow"></a>

1. QEMU loads kernel + initrd directly (no BIOS/UEFI)
2. Initrd `/init` loads virtio modules (blk, net, console, balloon)
3. Initrd mounts devtmpfs in new root, mounts `/dev/vda`
4. `switch_root` to real rootfs, systemd takes over
5. systemd-networkd brings up eth0 (DHCP via cloud-init)
6. Guest agent starts on the virtio-serial port (`/dev/vport*p*`)
7. Serial console via custom `microvm-console.service`

## Device transport <a href="#device-transport" id="device-transport"></a>

microvm with `pcie=on` uses PCI non-transitional devices:

| Device       | QEMU type                             | Guest sees                      |
| ------------ | ------------------------------------- | ------------------------------- |
| Block        | `virtio-blk-pci-non-transitional`     | `/dev/vda`                      |
| Network      | `virtio-net-pci-non-transitional`     | `eth0`                          |
| Serial/Agent | `virtio-serial-pci-non-transitional`  | `/dev/vport*p*`                 |
| Balloon      | `virtio-balloon-pci-non-transitional` | memory reporting                |
| vsock        | `vhost-vsock-pci-non-transitional`    | `/dev/vsock` (CID=VMID+1000)    |
| virtiofs     | `vhost-user-fs-pci`                   | `mount -t virtiofs shared /mnt` |
| Console      | ISA serial (`isa-serial=on`)          | `/dev/ttyS0`                    |

The kernel's built-in virtio-console driver creates the direct guest-agent port as `/dev/vport<bus>p<port>`. The index follows virtio probe order and is not stable across device sets: a trixie guest on PVE 9.1.9 gets `/dev/vport2p1`. Minimal root filesystems also never create the named udev symlink under `/dev/virtio-ports/`, so generated systemd guests start the single packaged agent service against whichever character device is actually present. The unit polls for the port rather than binding to a udev-generated device unit.

## Storage support <a href="#storage-support" id="storage-support"></a>

All PVE storage backends work:

| Storage        | Path QEMU sees                       | Format    |
| -------------- | ------------------------------------ | --------- |
| LVM / LVM-thin | `/dev/<vg>/vm-<vmid>-disk-0`         | raw       |
| ZFS            | `/dev/zvol/<pool>/vm-<vmid>-disk-0`  | raw       |
| Ceph/RBD       | `rbd:<pool>/vm-<vmid>-disk-0`        | rbd       |
| NFS/CIFS       | `/mnt/pve/<store>/images/<vmid>/...` | qcow2/raw |
| Local dir      | `/var/lib/vz/images/<vmid>/...`      | qcow2/raw |

For PVE-managed volumes, the command builder obtains the format from `PVE::Storage::parse_volname()`. This is required for file-backed linked clones: the clone is a qcow2 overlay even when the VM configuration does not contain an explicit `format=qcow2`. If PVE cannot identify the format, command generation fails rather than passing the disk to QEMU as raw.

Drive order is deterministic. `scsi0` is emitted first and appears as `/dev/vda`; an optional cloud-init disk at `scsi1` appears as `/dev/vdb`. Adding cloud-init does not require changing `root=/dev/vda`.

## Patch management <a href="#patch-management" id="patch-management"></a>

```bash
pve-microvm-patch status   # check
pve-microvm-patch apply    # apply (done on install)
pve-microvm-patch revert   # revert (done on removal)
```

* Originals backed up to `/usr/share/pve-microvm/backup/`
* dpkg trigger auto-reapplies after `qemu-server` upgrades

## Web UI integration <a href="#web-ui-integration" id="web-ui-integration"></a>

The `pve-microvm.js` extension (injected into `index.html.tpl`):

* Adds `microvm` to the machine type dropdown
* Hides unsupported fields in create wizard and hardware view
* Filters USB/PCI/BIOS/EFI/TPM rows from hardware panel
* Disables unsupported "Add hardware" menu items
* Shows ⚡ bolt icon for microvm-tagged VMs
* Adds "⚡ Clone microvm" context menu on templates

## Test infrastructure <a href="#test-infrastructure" id="test-infrastructure"></a>

| Node      | CPU                                     | RAM    | Role                                         |
| --------- | --------------------------------------- | ------ | -------------------------------------------- |
| **z83ii** | Intel Atom x5-Z8350, 4 cores @ 1.44 GHz | 2 GB   | Stability testing on worst-case hardware     |
| **borg**  | Intel Core i7-12700, 20 cores @ 4.9 GHz | 128 GB | Performance reference and multi-node testing |

Both nodes run PVE 9.1.7–9.1.9 with QEMU 10.1.2 and kernel 6.17.13-2-pve. The z83ii is deliberately used as the primary test node — if microvms work well on a 2 GB Atom, they'll work anywhere.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.layeronecloud.com/pve-microvm/architecture.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
