{"article":{"slug":"building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun","title":"Building a VMM from scratch, #0: Why BoxLite is replacing libkrun","subtitle":null,"summary":"BoxLite explains why every box runs in a micro-VM on libkrun today, why they are replacing it with an in-house VMM, and how this build-in-public series will walk through writing a VMM from scratch.","content_type":"blog_post","language":"en","canonical_url":"https://blog.boxlite.ai/building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun","author":{"name":"BoxLite","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"BoxLite","url":"https://blog.boxlite.ai/","listing_slug":null,"listing":null},"topics":[{"name":"Systems Programming","slug":"systems-programming","url":"https://listedarticles.com/topics/systems-programming"},{"name":"Infrastructure","slug":"infrastructure","url":"https://listedarticles.com/topics/infrastructure"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"}],"about_listings":[],"cover_image_url":null,"license":"CC0-1.0","word_count":984,"reading_minutes":4,"published_at":"2026-10-11T12:00:00.000Z","added_at":"2026-10-12T05:11:15.752Z","updated_at":"2026-10-12T05:11:15.752Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun","markdown_url":"https://listedarticles.com/articles/building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun.md","example":false,"citation":"BoxLite, BoxLite. \"Building a VMM from scratch, #0: Why BoxLite is replacing libkrun.\" 11 Oct 2026. https://blog.boxlite.ai/building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun (CC0-1.0)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://blog.boxlite.ai/building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun"},"body_markdown":"# Building a VMM from scratch, #0: Why BoxLite is replacing libkrun\n\nPart #0 of a build-in-public series: what BoxLite is, why we started on libkrun, why we're replacing it, and how a VMM works.\n\nEvery BoxLite box runs in its own micro-VM, and today libkrun runs that VM. We're replacing it with a VMM we write ourselves. This series builds it in public, one merged PR at a time.\n\n## 1\\. What BoxLite is\n\nBoxLite is the compute substrate for AI agents — light enough to embed on your laptop, elastic enough to power an agentic cloud. Each box is a hardware-isolated micro-VM that runs any OCI image and keeps its state across turns.\n\nFigure 1. One engine in two places: the embedded BoxLite runtime starts one micro-VM per box, on your machine or on a cloud runner.\n\nThe part that runs each micro-VM is the VMM, the virtual machine monitor. That part is the subject of this series.\n\n## 2\\. Why we started on libkrun\n\nlibkrun describes itself as \"a dynamic library that allows programs to easily acquire the ability to run processes in a partially isolated environment\", on KVM (Linux) and HVF (macOS on Apple silicon), behind \"a simple C API\". That fit BoxLite:\n\n  * A library, not a daemon: BoxLite embeds in your app, with no root and no background service.\n  * One API for KVM on Linux and Hypervisor.framework on macOS.\n  * The guest kernel comes with it (libkrunfw), along with virtio-fs, virtio-blk and vsock.\n\n\n\nlibkrun is how BoxLite runs boxes on macOS and Linux today.\n\n## 3\\. Why we're replacing it\n\nlibkrun is clear about what it isn't: its [README](<https://github.com/containers/libkrun#readme>) lists \"Become a generic VMM\" and \"Be compatible with all kinds of workloads\" as non-goals. BoxLite's needs grew past those goals:\n\n  * The VM's outcome. libkrun ends the process itself, so the caller gets no exit status, and every start failure comes back as \n        \n        -EINVAL\n\n.\n  * The kernel as a shared library. libkrunfw is loaded with \n        \n        dlopen\n\n, and that keeps breaking on some hosts; [#1512](<https://github.com/boxlite-ai/boxlite/issues/1512>) lists six such issues.\n  * Devices. Each directory volume gets its own device, and with three a box fails to start with \n        \n        IrqsExhausted\n\n([#935](<https://github.com/boxlite-ai/boxlite/issues/935>)).\n  * What comes next. Hot-plug mounts, live resizing, memory snapshots for AutoPause ([#1003](<https://github.com/boxlite-ai/boxlite/issues/1003>)) and GPUs all need a VMM we control.\n\n\n\n### What we're not doing\n\n  * Forking or patching libkrun. It ships unchanged until the new VMM is the default, stays one release as a fallback, and is then removed.\n  * Building a general-purpose VMM. It runs one VM per process with BoxLite's devices only; confidential computing, live migration and Intel Macs are out of scope.\n\n\n\n### What it costs\n\n  * The new VMM needs macOS 15 or later, for Hypervisor.framework's in-kernel interrupt controller. macOS 12–14 keep libkrun until cutover.\n  * A long road: first boot (M1), then a real box (M2), volumes (M3), hardening (M4) and cutover (M5).\n\n\n\n## 4\\. How a VMM works, in one screen\n\nFigure 2. Inside one machine. The runtime spawns a jailed boxlite-shim per box, and the VMM inside it runs the micro-VM. PROPOSED marks what this series replaces.\n\nA hypervisor (KVM or Hypervisor.framework) runs guest instructions on the CPU. The VMM is the ordinary process around it: it gives the guest memory, creates vCPUs, loads the kernel and emulates every device. Its heart is one loop:\n\nFigure 3. Run a vCPU until it exits, handle the exit, and run it again.\n\nThe first guest our new KVM backend ran was 7 bytes long:\n    \n    \n    mov dx, 0x3f8   ; COM1 serial port\n    mov al, 'K'\n    out dx, al      ; KVM exits to the VMM: port 0x3f8, byte 'K'\n    hlt\n    \n\nEverything else is detail, and each detail gets a part in this series:\n\nPiece| What it is| Part  \n---|---|---  \nMemory| Guest RAM is host memory; an unmapped address is a device| #1  \nExits| One exit contract for KVM, Hypervisor.framework and, later, WHP| #2  \nStopping| Pulling a vCPU out of the guest, safely| #3  \nCPU state| Registers, CPUID and MSRs on x86| #4  \nBoot| The VMM is the bootloader| #6  \nDevices| Serial, RTC and reset first, then virtio| #7, #10–12  \n  \n## 5\\. The series\n\nStatus as of October 11, 2026. A part goes out only after its code merges, so every claim links to a merged PR.\n\nArc| Parts| Ships when  \n---|---|---  \n1\\. First instructions| #1 Your first VM is 7 bytes · #2 Design the exit contract first · #3 Stopping a vCPU is the hard part · #4 x86 CPU state, by hand · #5 A kernel we build ourselves| Code merged  \n2\\. First boot (M1)| #6 You are the bootloader · #7 Boring devices first · #8 On Apple silicon, you decode the exits · #9 One kernel, three hosts| As M1 lands  \n3\\. First box (M2–M3)| #10 virtio from scratch · #11 Disks, sockets, network · #12 One virtio-fs device for every volume · #13 Our agent as PID 1| M2–M3  \n4\\. Ship it (M4–M5)| #14 Guest input must never panic the host · #15 Measured against libkrun · #16 Deleting libkrun| M4–M5  \nLater| Memory snapshots, hot-plug mounts, GPUs, our own network stack| After M5  \n  \n### Where we are\n\n  * ✅ Design and one hypervisor contract for KVM, Hypervisor.framework and, later, Windows\n  * ✅ KVM on x86_64: VMs, memory, the run loop, I/O completion, kicks, boot registers, CPUID and MSRs\n  * ✅ A reproducible build of our pinned Linux 6.12 kernel for x86_64\n  * ⏳ M1: first boot on macOS arm64, Linux x86_64 and Linux arm64, checked in CI\n\n\n\nFollow along on the [RSS feed](<https://blog.boxlite.ai/rss.xml>) and in every PR on [GitHub](<https://github.com/boxlite-ai/boxlite>).\n\n## 6\\. Come build it with us\n\nBoxLite is open source, and so is every step of this VMM. We'd love your help:\n\n  * Challenge the design. Read the [VMM design doc](<https://github.com/boxlite-ai/boxlite/blob/main/docs/contributing/architecture/vmm/README.md>) and tell us where it's wrong.\n  * Pick up M1 work. [#1698](<https://github.com/boxlite-ai/boxlite/issues/1698>) lists the open slices and their dependencies: devices, kernel loaders, the Hypervisor.framework backend, arm64 KVM and three-host CI.\n  * Ask and discuss. Open a [GitHub issue](<https://github.com/boxlite-ai/boxlite/issues>) or find us on [Discord](<https://go.boxlite.ai/discord>).\n\n\n\nStart with [CONTRIBUTING.md](<https://github.com/boxlite-ai/boxlite/blob/main/CONTRIBUTING.md>). Your first PR asks you to sign our contributor license agreement.","body_html":"<h1 id=\"building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun\">Building a VMM from scratch, #0: Why BoxLite is replacing libkrun</h1>\n<p>Part #0 of a build-in-public series: what BoxLite is, why we started on libkrun, why we&#39;re replacing it, and how a VMM works.</p>\n<p>Every BoxLite box runs in its own micro-VM, and today libkrun runs that VM. We&#39;re replacing it with a VMM we write ourselves. This series builds it in public, one merged PR at a time.</p>\n<h2 id=\"1-what-boxlite-is\">1. What BoxLite is</h2>\n<p>BoxLite is the compute substrate for AI agents — light enough to embed on your laptop, elastic enough to power an agentic cloud. Each box is a hardware-isolated micro-VM that runs any OCI image and keeps its state across turns.</p>\n<p>Figure 1. One engine in two places: the embedded BoxLite runtime starts one micro-VM per box, on your machine or on a cloud runner.</p>\n<p>The part that runs each micro-VM is the VMM, the virtual machine monitor. That part is the subject of this series.</p>\n<h2 id=\"2-why-we-started-on-libkrun\">2. Why we started on libkrun</h2>\n<p>libkrun describes itself as &quot;a dynamic library that allows programs to easily acquire the ability to run processes in a partially isolated environment&quot;, on KVM (Linux) and HVF (macOS on Apple silicon), behind &quot;a simple C API&quot;. That fit BoxLite:</p>\n<ul><li>A library, not a daemon: BoxLite embeds in your app, with no root and no background service.</li><li>One API for KVM on Linux and Hypervisor.framework on macOS.</li><li>The guest kernel comes with it (libkrunfw), along with virtio-fs, virtio-blk and vsock.</li></ul>\n<p>libkrun is how BoxLite runs boxes on macOS and Linux today.</p>\n<h2 id=\"3-why-we-re-replacing-it\">3. Why we&#39;re replacing it</h2>\n<p>libkrun is clear about what it isn&#39;t: its <a href=\"https://github.com/containers/libkrun#readme\" rel=\"nofollow ugc noopener\">README</a> lists &quot;Become a generic VMM&quot; and &quot;Be compatible with all kinds of workloads&quot; as non-goals. BoxLite&#39;s needs grew past those goals:</p>\n<ul><li><p>The VM&#39;s outcome. libkrun ends the process itself, so the caller gets no exit status, and every start failure comes back as </p><pre><code>-EINVAL</code></pre></li></ul>\n<p>.</p>\n<ul><li><p>The kernel as a shared library. libkrunfw is loaded with </p><pre><code>dlopen</code></pre></li></ul>\n<p>, and that keeps breaking on some hosts; <a href=\"https://github.com/boxlite-ai/boxlite/issues/1512\" rel=\"nofollow ugc noopener\">#1512</a> lists six such issues.</p>\n<ul><li><p>Devices. Each directory volume gets its own device, and with three a box fails to start with </p><pre><code>IrqsExhausted</code></pre></li></ul>\n<p>(<a href=\"https://github.com/boxlite-ai/boxlite/issues/935\" rel=\"nofollow ugc noopener\">#935</a>).</p>\n<ul><li>What comes next. Hot-plug mounts, live resizing, memory snapshots for AutoPause (<a href=\"https://github.com/boxlite-ai/boxlite/issues/1003\" rel=\"nofollow ugc noopener\">#1003</a>) and GPUs all need a VMM we control.</li></ul>\n<h3 id=\"what-we-re-not-doing\">What we&#39;re not doing</h3>\n<ul><li>Forking or patching libkrun. It ships unchanged until the new VMM is the default, stays one release as a fallback, and is then removed.</li><li>Building a general-purpose VMM. It runs one VM per process with BoxLite&#39;s devices only; confidential computing, live migration and Intel Macs are out of scope.</li></ul>\n<h3 id=\"what-it-costs\">What it costs</h3>\n<ul><li>The new VMM needs macOS 15 or later, for Hypervisor.framework&#39;s in-kernel interrupt controller. macOS 12–14 keep libkrun until cutover.</li><li>A long road: first boot (M1), then a real box (M2), volumes (M3), hardening (M4) and cutover (M5).</li></ul>\n<h2 id=\"4-how-a-vmm-works-in-one-screen\">4. How a VMM works, in one screen</h2>\n<p>Figure 2. Inside one machine. The runtime spawns a jailed boxlite-shim per box, and the VMM inside it runs the micro-VM. PROPOSED marks what this series replaces.</p>\n<p>A hypervisor (KVM or Hypervisor.framework) runs guest instructions on the CPU. The VMM is the ordinary process around it: it gives the guest memory, creates vCPUs, loads the kernel and emulates every device. Its heart is one loop:</p>\n<p>Figure 3. Run a vCPU until it exits, handle the exit, and run it again.</p>\n<p>The first guest our new KVM backend ran was 7 bytes long:</p>\n<pre><code>mov dx, 0x3f8   ; COM1 serial port\nmov al, &#39;K&#39;\nout dx, al      ; KVM exits to the VMM: port 0x3f8, byte &#39;K&#39;\nhlt</code></pre>\n<p>Everything else is detail, and each detail gets a part in this series:</p>\n<div class=\"table-wrap\"><table><thead><tr><th>Piece</th><th>What it is</th><th>Part</th></tr></thead><tbody><tr><td>Memory</td><td>Guest RAM is host memory; an unmapped address is a device</td><td>#1</td></tr><tr><td>Exits</td><td>One exit contract for KVM, Hypervisor.framework and, later, WHP</td><td>#2</td></tr><tr><td>Stopping</td><td>Pulling a vCPU out of the guest, safely</td><td>#3</td></tr><tr><td>CPU state</td><td>Registers, CPUID and MSRs on x86</td><td>#4</td></tr><tr><td>Boot</td><td>The VMM is the bootloader</td><td>#6</td></tr><tr><td>Devices</td><td>Serial, RTC and reset first, then virtio</td><td>#7, #10–12</td></tr></tbody></table></div>\n<h2 id=\"5-the-series\">5. The series</h2>\n<p>Status as of October 11, 2026. A part goes out only after its code merges, so every claim links to a merged PR.</p>\n<div class=\"table-wrap\"><table><thead><tr><th>Arc</th><th>Parts</th><th>Ships when</th></tr></thead><tbody><tr><td>1. First instructions</td><td>#1 Your first VM is 7 bytes · #2 Design the exit contract first · #3 Stopping a vCPU is the hard part · #4 x86 CPU state, by hand · #5 A kernel we build ourselves</td><td>Code merged</td></tr><tr><td>2. First boot (M1)</td><td>#6 You are the bootloader · #7 Boring devices first · #8 On Apple silicon, you decode the exits · #9 One kernel, three hosts</td><td>As M1 lands</td></tr><tr><td>3. First box (M2–M3)</td><td>#10 virtio from scratch · #11 Disks, sockets, network · #12 One virtio-fs device for every volume · #13 Our agent as PID 1</td><td>M2–M3</td></tr><tr><td>4. Ship it (M4–M5)</td><td>#14 Guest input must never panic the host · #15 Measured against libkrun · #16 Deleting libkrun</td><td>M4–M5</td></tr><tr><td>Later</td><td>Memory snapshots, hot-plug mounts, GPUs, our own network stack</td><td>After M5</td></tr></tbody></table></div>\n<h3 id=\"where-we-are\">Where we are</h3>\n<ul><li>✅ Design and one hypervisor contract for KVM, Hypervisor.framework and, later, Windows</li><li>✅ KVM on x86_64: VMs, memory, the run loop, I/O completion, kicks, boot registers, CPUID and MSRs</li><li>✅ A reproducible build of our pinned Linux 6.12 kernel for x86_64</li><li>⏳ M1: first boot on macOS arm64, Linux x86_64 and Linux arm64, checked in CI</li></ul>\n<p>Follow along on the <a href=\"https://blog.boxlite.ai/rss.xml\" rel=\"nofollow ugc noopener\">RSS feed</a> and in every PR on <a href=\"https://github.com/boxlite-ai/boxlite\" rel=\"nofollow ugc noopener\">GitHub</a>.</p>\n<h2 id=\"6-come-build-it-with-us\">6. Come build it with us</h2>\n<p>BoxLite is open source, and so is every step of this VMM. We&#39;d love your help:</p>\n<ul><li>Challenge the design. Read the <a href=\"https://github.com/boxlite-ai/boxlite/blob/main/docs/contributing/architecture/vmm/README.md\" rel=\"nofollow ugc noopener\">VMM design doc</a> and tell us where it&#39;s wrong.</li><li>Pick up M1 work. <a href=\"https://github.com/boxlite-ai/boxlite/issues/1698\" rel=\"nofollow ugc noopener\">#1698</a> lists the open slices and their dependencies: devices, kernel loaders, the Hypervisor.framework backend, arm64 KVM and three-host CI.</li><li>Ask and discuss. Open a <a href=\"https://github.com/boxlite-ai/boxlite/issues\" rel=\"nofollow ugc noopener\">GitHub issue</a> or find us on <a href=\"https://go.boxlite.ai/discord\" rel=\"nofollow ugc noopener\">Discord</a>.</li></ul>\n<p>Start with <a href=\"https://github.com/boxlite-ai/boxlite/blob/main/CONTRIBUTING.md\" rel=\"nofollow ugc noopener\">CONTRIBUTING.md</a>. Your first PR asks you to sign our contributor license agreement.</p>","headings":[{"level":1,"text":"Building a VMM from scratch, #0: Why BoxLite is replacing libkrun","id":"building-a-vmm-from-scratch-0-why-boxlite-is-replacing-libkrun"},{"level":2,"text":"1. What BoxLite is","id":"1-what-boxlite-is"},{"level":2,"text":"2. Why we started on libkrun","id":"2-why-we-started-on-libkrun"},{"level":2,"text":"3. Why we're replacing it","id":"3-why-we-re-replacing-it"},{"level":3,"text":"What we're not doing","id":"what-we-re-not-doing"},{"level":3,"text":"What it costs","id":"what-it-costs"},{"level":2,"text":"4. How a VMM works, in one screen","id":"4-how-a-vmm-works-in-one-screen"},{"level":2,"text":"5. The series","id":"5-the-series"},{"level":3,"text":"Where we are","id":"where-we-are"},{"level":2,"text":"6. Come build it with us","id":"6-come-build-it-with-us"}]}}