> 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.md).

# pve-microvm-secure

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

![pve-microvm](https://2514064782-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjfPvzyEy20mIFK8HxUJP%2Fuploads%2FSQbAbhutdbaOmnKhgsd4%2Fpve-microvm-icon-256.png?alt=media)

![pve-microvm in the Proxmox web UI](https://2514064782-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjfPvzyEy20mIFK8HxUJP%2Fuploads%2F4uGvx0d9fXVYzSHtbFG8%2Fpve-microvm-demo.gif?alt=media)

LayerOne's hardened build of [rcarmo/pve-microvm](https://github.com/rcarmo/pve-microvm): a Debian package that adds QEMU `microvm` machine type support to Proxmox VE. Runs OCI container images, [Firecracker rootfs images](/pve-microvm/firecracker.md), unikernels and alternative OS as lightweight hardware-isolated VMs.

Upstream built this for running coding agents and semi-trusted workloads on a homelab. This fork adapts it for **untrusted tenants on shared hardware**, which is a different threat model and a different set of defaults.

> **⚠️ This package patches `qemu-server` internals.** It rewrites the code path that starts *every* VM on the node. The patcher writes atomically, refuses layouts it does not recognise, and installs nothing that fails `perl -c` — but install it on a node with no customer VMs first and work through [docs/lab-verification.md](/pve-microvm/lab-verification.md).

❓ [FAQ](/pve-microvm/faq.md) · 🔒 [Security model](/pve-microvm/security.md) · 📝 [Upstream blog post](https://taoofmac.com/space/blog/2026/06/18/1845)

***

## Why <a href="#why" id="why"></a>

Something between LXC containers and full QEMU VMs, for running code you did not write.

|                | LXC                       | microvm                   | Standard VM         |
| -------------- | ------------------------- | ------------------------- | ------------------- |
| Isolation      | Namespace (shared kernel) | **KVM (own kernel)**      | KVM (own kernel)    |
| Boot time      | \~50 ms                   | **< 300 ms**              | 2–10 s              |
| Overhead       | Minimal                   | **Minimal**               | Moderate            |
| Attack surface | Broad (host kernel)       | **Minimal (virtio-pcie)** | Broad (emulated PC) |
| Untrusted code | ⚠️ risky                  | **✅ safe**                | ✅ safe              |

**Hardware-isolated VMs with container-like speed**, managed through the same Proxmox tools you already use. No new runtime — QEMU's `microvm` machine type is already on every PVE node.

***

## What this fork changes <a href="#what-this-fork-changes" id="what-this-fork-changes"></a>

Every one of these is a default that was safe for a homelab and is not safe for tenants. Each is explained, with the reasoning, in [docs/security.md](/pve-microvm/security.md).

|                          | Upstream                                                      | This fork                                                                       |
| ------------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| **Kernel config**        | `args` on every guest, which PVE restricts to `root@pam`      | **Boot profiles** selected by machine type — needs `VM.Config.HWType`, not root |
| **Root password**        | Empty (`root::`) in every built image                         | **Locked**; credentials injected per clone, or `--ssh-key` at build             |
| **Serial console**       | `agetty --autologin root`                                     | **Login prompt**; `--dev-autologin` opts back in and marks the image            |
| **sshd**                 | Distro default                                                | `PermitRootLogin no`, no password auth, no empty passwords                      |
| **vsock**                | On for every guest                                            | **Off** unless a profile enables it                                             |
| **SSH agent forwarding** | `socat VSOCK-LISTEN` — reachable by *every* guest on the host | Peer CID verified per connection                                                |
| **Downloads**            | Kernel, rootfs images and busybox over plain `curl`           | **Pinned digests**, fail closed; OCI images resolved to `@sha256:`              |
| **Releases**             | Unsigned; docs piped an unverified URL into a root install    | `SHA256SUMS` + build provenance attestation                                     |
| **Patching qemu-server** | Truncate-and-rewrite in place                                 | Atomic, `perl -c` preflight, refuses unknown layouts, never blocks removal      |
| **9p shares**            | Any `security_model`, any path                                | Mapped models only, system paths refused                                        |
| **`cpu` option**         | Silently discarded, always `-cpu host`                        | Honoured                                                                        |
| **virtio-rng**           | Refused outright                                              | Available                                                                       |
| **QEMU seccomp**         | Off                                                           | On by default                                                                   |

The headline is the first row. Because upstream required `args` on every microvm, and `args` is root-only in PVE, any automation that created or edited a guest needed a `root@pam` token — and since `args` is appended last to the QEMU command line, whoever could write it could append `-fsdev local,path=/,security_model=passthrough` to a process running as root on the host. Boot profiles remove both problems.

***

## Testing <a href="#testing" id="testing"></a>

Upstream shipped 80 checks. This fork has **162**, and the new ones cover the things that would be expensive to get wrong.

| Suite                                                                                                               | Assertions | Covers                                                                                                                                        |
| ------------------------------------------------------------------------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| [`test-boot-profiles.pl`](https://github.com/LayerOne-LLC/pve-microvm-secure/blob/main/tests/test-boot-profiles.pl) | 27         | Profile resolution, path-traversal refusal, image-root confinement, shipped profiles                                                          |
| [`test-patcher.py`](https://github.com/LayerOne-LLC/pve-microvm-secure/blob/main/tests/test-patcher.py)             | 21         | Idempotency, duplicate healing, refusing moved anchors and changed `config_to_command` signatures, never writing a file that fails to compile |
| [`test-credentials.sh`](https://github.com/LayerOne-LLC/pve-microvm-secure/blob/main/tests/test-credentials.sh)     | 34         | The credential script **executed** against a fake rootfs — both the `chpasswd` and busybox-`awk` paths — plus source-level guards             |
| Upstream suite                                                                                                      | 80         | Argument parsing, storage format detection, packaging contracts, docs                                                                         |

The credential tests run the real script rather than grepping for strings, so they verify that a `$6$` crypt hash survives two rounds of shell quoting and that root ends up locked rather than empty. The patcher tests drive the real patcher against realistic `Machine.pm` and `QemuServer.pm` fixtures and assert it leaves them **byte-identical** when it refuses.

CI pins every action to a commit SHA, publishes `SHA256SUMS` and attaches a build provenance attestation to each release.

### What is not yet tested <a href="#what-is-not-yet-tested" id="what-is-not-yet-tested"></a>

Automated tests cannot boot a guest, survive a `qemu-server` upgrade, or prove the PVE firewall reaches a microvm tap. [`tools/lab-verify.sh`](https://github.com/LayerOne-LLC/pve-microvm-secure/blob/main/tools/lab-verify.sh) drives those on real hardware — nine phases, including a canary VM created *before* patching so that "a plain guest still starts" means something.

**Until that checklist passes on your hardware, treat this as unverified.**

***

## Quick start <a href="#quick-start" id="quick-start"></a>

```bash
# Download the release assets, including the checksum file.
gh release download --repo LayerOne-LLC/pve-microvm-secure --pattern 'pve-microvm_*.deb' --pattern 'SHA256SUMS'

# Verify before installing as root. The checksum check is required; the
# provenance attestation is published when the repository's plan supports
# it, so treat a "no attestation found" as informational, not a failure.
sha256sum -c SHA256SUMS
gh attestation verify pve-microvm_*.deb --repo LayerOne-LLC/pve-microvm-secure || true

dpkg -i pve-microvm_*.deb
pve-microvm-patch verify        # confirms the patched qemu-server compiles

# Create a template from any OCI image. The image is pinned to its digest,
# and the root account is locked -- see docs/security.md.
pve-microvm-template --image debian:trixie-slim --ssh-key ~/.ssh/id_ed25519.pub

# Clone and boot. No `args`, so no root token is needed.
qm clone 9000 901 --name my-sandbox --full
qm start 901
qm terminal 901
```

> Boot settings come from a **boot profile** selected by the machine type (`microvm`, `microvm-paas`, …), not from the root-only `args` option. Profiles live in `/etc/pve-microvm/profiles/`.

Or use the **Create µVM** button in the PVE web UI.

***

## Supported guests <a href="#supported-guests" id="supported-guests"></a>

| Category             | Images                                                        |
| -------------------- | ------------------------------------------------------------- |
| **Linux (apt)**      | Debian, Ubuntu                                                |
| **Linux (apk)**      | Alpine                                                        |
| **Linux (dnf/tdnf)** | Fedora, Rocky, Alma, Amazon, Oracle, UBI, Photon, Azure Linux |
| **Router/Firewall**  | OpenWrt, OPNsense                                             |
| **BSD**              | SmolBSD (NetBSD, 31 ms boot)                                  |
| **Plan 9**           | 9Front                                                        |
| **Unikernel**        | OSv, gokrazy                                                  |
| **Compatible**       | Any Firecracker rootfs (ext4 import)                          |

Specialist images are not pinned by default. `fetch_verified` fails closed, so add a digest to `doc/microvm-checksums.conf` before building those templates.

***

## What's included <a href="#whats-included" id="whats-included"></a>

| Component                   | Description                                                     |
| --------------------------- | --------------------------------------------------------------- |
| **`pve-microvm-template`**  | Create PVE templates from OCI images or specialist OS           |
| **`pve-oci-import`**        | Convert any OCI image to a bootable microvm disk                |
| **`pve-microvm-share`**     | Share host directories via virtiofs                             |
| **`pve-microvm-9p`**        | Share host directories via 9p (no daemon)                       |
| **`pve-microvm-ssh-agent`** | Forward an SSH agent over vsock, to one verified guest          |
| **`pve-microvm-run`**       | Ephemeral microvms (run and destroy)                            |
| **`pve-microvm-bench`**     | Boot time and overhead benchmarking                             |
| **`lab-verify.sh`**         | Hardware verification: install, boot, privsep, upgrade, removal |
| **Boot profiles**           | `default`, `paas`, `netbsd` — the replacement for `args`        |
| **Web UI**                  | Create µVM dialog, ⚡ icon, xterm.js console, panel hiding       |
| **Kernel**                  | Pre-built 6.12.22 with PCIe virtio + vsock + virtiofs           |

***

## Hardware <a href="#hardware" id="hardware"></a>

Upstream tests on a 4-node cluster from an Atom x5-Z8350 (2 GB) to an i7-12700 (128 GB) — see [Cluster Hardware](/pve-microvm/cluster-hardware.md). That covers the microvm functionality this fork inherits.

The changes in this fork have their own verification path ([docs/lab-verification.md](/pve-microvm/lab-verification.md)) and are signed off per node, not inherited.

***

## Documentation <a href="#documentation" id="documentation"></a>

* [**Security model**](/pve-microvm/security.md) — every changed default and why
* [**Lab verification**](/pve-microvm/lab-verification.md) — the hardware checklist
* [**Installation**](/pve-microvm/installation.md) — install, verify, uninstall
* [**Quick Start**](/pve-microvm/usage.md) — templates, cloning, basic usage
* [**Guest OS**](/pve-microvm/guests.md) — supported distributions and specialist OS
* [**Networking & Storage**](/pve-microvm/networking.md) — virtiofs, 9p, vsock, SSH agent
* [**Web UI**](/pve-microvm/webui.md) — Create µVM dialog, console, icons
* [**Configuration**](/pve-microvm/configuration.md) — supported/unsupported options
* [**Architecture**](/pve-microvm/architecture.md) — how it works, QEMU command line
* [**Firecracker Compatibility**](/pve-microvm/firecracker.md) — importing rootfs images
* [**High Availability**](/pve-microvm/ha.md) — migration, HA relocate
* [**Known Issues**](/pve-microvm/known-issues.md) — workarounds and fixes
* [**Limitations**](/pve-microvm/limitations.md) — what doesn't work (yet)
* [**Troubleshooting**](/pve-microvm/troubleshooting.md) — common problems
* [**Development**](/pve-microvm/development.md) — repo structure, building
* [**Changelog**](/pve-microvm/changelog.md) — full feature list and release history

***

## Roadmap <a href="#roadmap" id="roadmap"></a>

| Feature                                           | Priority |
| ------------------------------------------------- | -------- |
| Sign off the hardware checklist on LayerOne nodes | High     |
| Network off by default                            | Medium   |
| Egress allow-list (nftables)                      | Medium   |
| Per-tenant image registry integration             | Medium   |
| Declarative VM config (TOML)                      | Low      |
| GPU passthrough                                   | Low      |
| AArch64 guest support                             | Low      |

***

## License and attribution <a href="#license-and-attribution" id="license-and-attribution"></a>

[Apache-2.0](https://github.com/LayerOne-LLC/Documentation/tree/main/untitled/.gitbook/assets/pve-microvm-LICENSE.txt). See [NOTICE](https://github.com/LayerOne-LLC/Documentation/tree/main/untitled/.gitbook/assets/pve-microvm-NOTICE.txt) for the full attribution.

The microvm integration, the web UI, the kernel configuration and the guest support matrix are [rcarmo/pve-microvm](https://github.com/rcarmo/pve-microvm) by Rui Carmo and contributors, Copyright 2026 Rui Carmo.

The security hardening, the test suites and the verification tooling described above are Copyright 2026 LayerOne LLC, licensed under the same terms. Files changed from upstream carry a notice to that effect, per section 4(b) of the License.

Apache-2.0 is the licence under which this code may be used and redistributed at all, and it requires that the licence and the attribution travel with any copy. Both ship inside the package at `/usr/share/doc/pve-microvm/`.

## Project artwork <a href="#project-artwork" id="project-artwork"></a>

[Full-resolution icon](https://github.com/LayerOne-LLC/Documentation/tree/main/untitled/.gitbook/assets/pve-microvm-icon.png)


---

# 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.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.
