# Nested virtualization recipe

URL: https://briq.cloud/cookbook/kvm-sandbox

> A sandbox with /dev/kvm, to run a hypervisor of your own, for example Firecracker, and the microVMs it starts. The same as the sandbox recipe: briq_exec for commands, `steps` at boot. Slower to wake: every start is a cold boot, with no memory kept. Default size large (4 CPUs, 8 GB); Pro and Team plans.

Tags: sandbox, testing

Guide:

- [Run microVMs in a KVM sandbox](https://briq.cloud/docs/guides/run-microvms-in-a-kvm-sandbox.md): Give the agent a sandbox with /dev/kvm, to run a hypervisor of its own such as Firecracker and the microVMs it starts.

## Run it

CLI:

```bash
briq run kvm-sandbox
```

MCP:

```ts
briq_run({ recipe: "kvm-sandbox", wait: true })
```

REST:

```bash
curl -X POST https://api.briq.cloud/v1/briqs \
  -H "Authorization: Bearer $BRIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "recipe": "kvm-sandbox",
    "wait": true
  }'
# → { "briqs": [{ "id": "bq_…", … }], "outputs": { "SANDBOX_HOST": "…" }, … }

# ... when done, for each briq id
curl -X DELETE https://api.briq.cloud/v1/briqs/<id> \
  -H "Authorization: Bearer $BRIQ_API_KEY"
```

briq.yaml:

```yaml
stack: app
services:
  sandbox:
    recipe: kvm-sandbox
    # its defaults, each a field you can set:
    # image: ghcr.io/briq-cloud/sandbox:kvm
    # size: large
    # volume: { gb: 40 }
```

## Fields

Set them beside `recipe`, in briq_run or a briq.yaml service, as a GitHub Actions service.

| Name | Type | Options | Default | What it does |
| --- | --- | --- | --- | --- |
| `image` | string | — | "ghcr.io/briq-cloud/sandbox:kvm" | Another tag of ghcr.io/briq-cloud/sandbox, or any image with sh. The boot runs kvm-ready, when the image has it, before the steps |
| `env` | record | { key: string \| { secret, value?, literal?, scope?, hosts?, locations? } } | {} | Added to the sandbox's environment. A credential as { secret: NAME, value }. One only the steps need (a clone, a private registry) as { secret: NAME, value, scope: "boot" }: a variable of the steps, then gone, never in the environment briq_exec runs in. A credential the code calls an API with, as { secret: NAME, value, hosts: ["api.stripe.com"] }: a placeholder here, the real value put in by the team's credential gateway on the way to those hosts only. A team secret (briq_secrets) as { secret: NAME, scope: "team" }: the same, with the value held by the team |
| `steps` | object[] | — | — | Shell commands run in /work at each start, before the sandbox is ready: [{ run: "git clone https://x:$GITHUB_TOKEN@github.com/acme/shop ." }, { run: "npm ci" }]. A failed step fails the start; briq_logs shows why |
| `cmd` | string \| string[] | — | — | The long-lived process, after the steps. Default: none (the sandbox waits for commands). A server here keeps serving on $PORT |
| `size` | enum | pico, micro, nano, small, medium, large, xlarge | "large" | large by default. xlarge for a full test suite or many VMs at once |
| `volume` | object | { gb? } | {"gb":40} | The volume at /work: { gb } |
| `files` | record | { key: string \| { base64 } } | — | Boot files, written at each start: { "/work/check.py": "…" } |

## Outputs

Returned by `briq_run` once the stack is healthy. Hosts resolve inside your team network; secrets are generated per run.

| Output | Value |
| --- | --- |
| `SANDBOX_HOST` | `sandbox.<stack>.internal` |
| `WORKDIR` | `/work` |
| `PORT` | `8080` |

## What it runs

| Briq | Image | Ports | Size | Volume |
| --- | --- | --- | --- | --- |
| sandbox | `ghcr.io/briq-cloud/sandbox:kvm` | 8080 | large | 40 GB at /work |

## When to use it

* Code that runs a hypervisor of your own, for example Firecracker, and the microVMs it starts.
* A test suite or a build that boots VMs.
* Anything the [sandbox](https://briq.cloud/cookbook/sandbox) does, with `/dev/kvm` in it.

Step by step: [Run microVMs in a KVM sandbox](https://briq.cloud/docs/guides/run-microvms-in-a-kvm-sandbox).

```
briq_run({ recipe: "kvm-sandbox" })
```

## Tips

* **Same params as the sandbox**: `steps`, `env`, `files`, `cmd`, `volume`. Boot-only secrets and the
  ready marker work the same way.
* **`large` by default** (4 CPUs, 8 GB). `xlarge` (8 CPUs, 16 GB) for a full test suite or many VMs
  at once. Prices: [pricing](https://briq.cloud/pricing).
* **Every start is a cold boot**: no memory is kept while it sleeps. A wake takes the time of a
  boot, and the `steps` run again.
* **Keep work on `/work`**: the volume (40 GB by default) is kept while it sleeps. A fork copies the
  disk only.
* **The image readies `/dev/kvm`**: the boot runs `kvm-ready` when the image has it, before the
  steps. Another image has to ready `/dev/kvm` itself.
* **Pro and Team plans**: a Hobby team gets a `plan_limit` error.
* **Not everywhere**: `briq_catalog` lists it only where `/dev/kvm` is available. Otherwise use the
  [sandbox](https://briq.cloud/cookbook/sandbox).
* **Test with `ls -l /dev/kvm`** first: `briq_exec` with `["ls", "-l", "/dev/kvm"]`.
