Fork() for AI agent microVMs. Spawn 100 children in ~100ms from a warm parent; BRANCH a live VM in ~150ms. KVM-isolated, snapshot CoW.
$git clone https://github.com/deeplethe/forkdInstalls into the current project.
Install forkd by running `git clone https://github.com/deeplethe/forkd`, then use it for the current task and follow its documentation at https://github.com/deeplethe/forkd.
| 1 | <br/> |
| 2 | |
| 3 | <div align="center"> |
| 4 | <picture> |
| 5 | <source media="(prefers-color-scheme: dark)" srcset="docs/logo-dark.svg"> |
| 6 | <img alt="forkd" src="docs/logo.svg" width="220"> |
| 7 | </picture> |
| 8 | </div> |
| 9 | |
| 10 | <br/> |
| 11 | <br/> |
| 12 | |
| 13 | <p align="center"> |
| 14 | <a href="https://github.com/deeplethe/forkd/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/deeplethe/forkd/ci.yml?branch=main&style=flat-square&label=ci"></a> |
| 15 | <a href="https://github.com/deeplethe/forkd/releases"><img alt="Release" src="https://img.shields.io/github/v/release/deeplethe/forkd?style=flat-square&color=4c956c"></a> |
| 16 | <a href="https://pypi.org/project/forkd/"><img alt="PyPI" src="https://img.shields.io/pypi/v/forkd?style=flat-square&color=3776ab&logo=pypi&logoColor=white"></a> |
| 17 | <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square"></a> |
| 18 | <a href="./README-zh.md"><img alt="中文 README" src="https://img.shields.io/badge/README-%E4%B8%AD%E6%96%87-red?style=flat-square"></a> |
| 19 | <a href="https://github.com/deeplethe/forkd/stargazers"><img alt="Stars" src="https://img.shields.io/github/stars/deeplethe/forkd?style=flat-square&color=eab308&logo=github"></a> |
| 20 | </p> |
| 21 | |
| 22 | <br/> |
| 23 | |
| 24 | ## Fork 100 microVMs in 101 ms. BRANCH a live VM in 56 ms (v0.4 live mode). |
| 25 | |
| 26 | <div align="center"> |
| 27 | <img alt="forkd: pull a portable snapshot from the hub, then fork 100 microVMs" src="docs/assets/quickstart-fork-100.gif" width="760"> |
| 28 | <br/> |
| 29 | <em>Unedited: `forkd pull` a portable snapshot, then fork 100 microVMs — 100/100 alive, ~200 ms wall-clock. <a href="docs/assets/quickstart-fork-100.cast">asciicast</a>. (First pull also fetches the rootfs sidecar once; cached here.)</em> |
| 30 | </div> |
| 31 | |
| 32 | <br/> |
| 33 | |
| 34 | A microVM sandbox runtime for **AI agent fan-out**. Children fork |
| 35 | from a warmed parent snapshot, inheriting its address space |
| 36 | copy-on-write instead of cold-booting their own kernel. |
| 37 | |
| 38 | forkd is built on Firecracker. The parent VM boots once, imports |
| 39 | your runtime (Python + your dependencies, a JIT-warmed JVM, an |
| 40 | already-loaded ML model) and is paused to disk. Each child is a |
| 41 | separate Firecracker process that `mmap`s the parent's memory image |
| 42 | with `MAP_PRIVATE`; the kernel implements copy-on-write at the page |
| 43 | level, so children share the parent's resident memory until they |
| 44 | diverge. |
| 45 | |
| 46 | The result is two properties at once: per-child KVM isolation, and a |
| 47 | spawn cost that's closer to `fork(2)` than to a cold-boot VM. |
| 48 | |
| 49 | forkd also supports **BRANCH**: pause a running sandbox, snapshot its |
| 50 | in-flight state, and resume — all in ~150 ms — so an agent can fork |
| 51 | mid-thought, not only at warm-up. v0.3.4 fixed a slow-path regression |
| 52 | where repeated BRANCHes on the same parent ballooned from 150 ms to |
| 53 | 2.7 s ([#146](https://github.com/deeplethe/forkd/issues/146)); the |
| 54 | chain now stays flat (17.6× faster on the 6th consecutive BRANCH). |
| 55 | |
| 56 | **v0.4 live BRANCH** collapses the source-pause window from ~200 ms |
| 57 | (Diff) to **56 ms p50 / 64 ms p90** on a 1.5 GiB source — measured |
| 58 | on a real BRANCH workload, [`bench/live-fork-pause-window/RESULTS-v0.4.md`](./bench/live-fork-pause-window/RESULTS-v0.4.md). |
| 59 | **3.6× faster pause** vs v0.3 Diff at p50, and the gap *widens* on |
| 60 | slower storage because Live's pause is disk-independent (memory |
| 61 | copy runs after resume, not during). With `wait: false` the caller |
| 62 | returns in ~70 ms while the background copy completes asynchronously |
| 63 | — a **200×** RT improvement for fire-and-forget BRANCH from agent |
| 64 | code. Pass `--live` / `--no-wait` on the CLI, `mode: "live"` / |
| 65 | `wait: false` on REST, or the same on the Python / TypeScript / MCP |
| 66 | SDKs. |
| 67 | |
| 68 | ```python |
| 69 | from forkd import Controller |
| 70 | c = Controller() |
| 71 | # Source must boot with live_fork=True (memfd-backed RAM, the prereq |
| 72 | # for UFFD_WP to see writes from the running parent). |
| 73 | parent = c.spawn_sandboxes("pyagent", n=1, live_fork=True)[0] |
| 74 | # ... drive parent ... then BRANCH live + fire-and-forget: |
| 75 | branch = c.branch_sandbox(parent["id"], mode="live", wait=False) |
| 76 | # Returns after ~10 ms with status="writing"; poll list_sna |