Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/deeplethe/forkd/llms.txt

Use this file to discover all available pages before exploring further.

Sandboxes are live Firecracker child VMs forked copy-on-write from a registered snapshot. Each child is a fully-isolated KVM microVM with its own vCPU state, network namespace, and cgroup memory limit, but shares the parent snapshot’s memory pages until it diverges. Forking 100 sandboxes from a warm parent takes roughly 100 ms on typical hardware — closer to fork(2) than to a cold VM boot. Once a sandbox is running, you can execute subprocesses inside it, evaluate Python expressions against the already-warmed PID 1 interpreter, and branch the live VM into a new snapshot so other sandboxes can fork from that mid-execution checkpoint.

POST /v1/sandboxes

Fork n children from a registered snapshot tag. All children start from the same warm parent state copy-on-write. Returns an array of SandboxInfo objects, one per child. Request body
snapshot_tag
string
required
The registered snapshot tag to fork from. Returns 404 if no such tag is registered.
n
integer
default:"1"
Number of children to fork in this call. Must be between 1 and 1000 inclusive.
per_child_netns
boolean
default:"false"
When true, place each child in its own pre-provisioned network namespace (forkd-child-<i>). The host must have created those namespaces before forking via scripts/netns-setup.sh N.When false, all children share the host’s default network namespace. This is fine for local development but means children can observe each other’s network traffic.
memory_limit_mib
integer
Optional. Set a memory.max cgroup v2 limit in MiB for each child. Requires cgroup v2 unified hierarchy and write access to /sys/fs/cgroup/forkd/. When not set, children have no memory cap beyond available host RAM.
live_fork
boolean
default:"false"
v0.4+. Spawn each sandbox with a memfd-backed RAM region instead of a file-backed one. Required for later POST /v1/sandboxes/:id/branch calls using mode: "live" — UFFD_WP only works on shmem/memfd-backed VMAs.Requires Linux ≥ 5.7 and the vendored Firecracker fork. Run forkd doctor to verify both prerequisites are met.Sandboxes spawned without live_fork: true can still take full and diff branches; they just cannot take live branches.
Response — 201 Created An array of SandboxInfo objects, one per forked child.
[
  {
    "id": "sb-67a1b3-0000",
    "snapshot_tag": "py",
    "netns": "forkd-child-1",
    "guest_addr": "10.42.0.2:8888",
    "created_at_unix": 1717000123,
    "pid": 314159,
    "memory_limit_mib": 256
  },
  {
    "id": "sb-67a1b3-0001",
    "snapshot_tag": "py",
    "netns": "forkd-child-2",
    "guest_addr": "10.42.0.3:8888",
    "created_at_unix": 1717000123,
    "pid": 314160,
    "memory_limit_mib": 256
  }
]
Errors
StatusCause
400 Bad Requestn is out of range, or request body is malformed.
404 Not Foundsnapshot_tag is not registered.
500 Internal Server ErrorFirecracker restore failure — check daemon logs.
curl example
TOKEN=$(cat /etc/forkd/token)

curl -s \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -X POST http://127.0.0.1:8889/v1/sandboxes \
  -d '{
    "snapshot_tag": "py",
    "n": 5,
    "per_child_netns": true,
    "memory_limit_mib": 256
  }'

GET /v1/sandboxes

List all currently active sandboxes. Returns an array of SandboxInfo objects. Response — 200 OK
[
  {
    "id": "sb-67a1b3-0000",
    "snapshot_tag": "py",
    "netns": "forkd-child-1",
    "guest_addr": "10.42.0.2:8888",
    "created_at_unix": 1717000123,
    "pid": 314159,
    "memory_limit_mib": 256
  }
]

GET /v1/sandboxes/:id

Fetch metadata for a single sandbox. Path parameter
id
string
required
The sandbox ID (e.g. sb-67a1b3-0000).
Response — 200 OK Returns a single SandboxInfo object. Errors
StatusCause
404 Not FoundNo sandbox with that ID is currently alive.

DELETE /v1/sandboxes/:id

Terminate a sandbox. Kills the Firecracker process and removes the cgroup leaf for that child. Returns 204 No Content. Path parameter
id
string
required
The sandbox ID to terminate.
Termination does not delete any snapshot that was branched from this sandbox. Snapshots produced by POST /v1/sandboxes/:id/branch are independent of the source sandbox’s lifecycle and persist until explicitly deleted via DELETE /v1/snapshots/:tag.
Errors
StatusCause
404 Not FoundNo sandbox with that ID is alive.

POST /v1/sandboxes/:id/ping

Round-trip to the guest agent (PID 1) inside the VM. Use this to verify the sandbox is healthy and to confirm which runtime version is loaded. Path parameter
id
string
required
The sandbox ID to ping.
Response — 200 OK
{ "pong": true, "numpy_version": "1.26.4", "pid": 1 }
pong
boolean
required
Always true on a healthy response.
numpy_version
string
required
The numpy version imported in the warmed PID 1 interpreter. Useful for verifying the snapshot’s runtime version without spawning a subprocess.
pid
integer
required
PID of the in-guest agent process. Always 1 when the agent runs as the init process.

POST /v1/sandboxes/:id/exec

Spawn a subprocess inside the sandbox and capture its output. The command runs as a fresh child process, so it pays the full subprocess startup cost including any Python import time.
For quick Python expression evaluation against the already-warmed interpreter, use POST /v1/sandboxes/:id/eval instead — it reuses PID 1 and is ~96× faster (1 ms vs 96 ms) because it skips the subprocess cold-start.
Path parameter
id
string
required
The sandbox ID to execute in.
Request body
args
string[]
required
Argv list for the subprocess, e.g. ["python3", "-c", "print(2+2)"].
timeout_secs
integer
default:"30"
Maximum time in seconds to wait for the subprocess to exit before the daemon kills it and returns an error.
Response — 200 OK
{ "stdout": "4\n", "stderr": "", "exit_code": 0 }
stdout
string
required
Captured standard output from the subprocess.
stderr
string
required
Captured standard error from the subprocess.
exit_code
integer
required
Exit status of the subprocess. 0 on success.

POST /v1/sandboxes/:id/eval

Evaluate a Python expression against the already-warmed interpreter running as PID 1 inside the sandbox. Because this reuses the existing interpreter process rather than spawning a new one, it avoids the full Python startup and import cost.
PathLatencyMechanism
eval~1 msReuses warmed PID 1 interpreter
exec python3 -c ...~96 msCold subprocess re-imports everything
Path parameter
id
string
required
The sandbox ID to evaluate in.
Request body
code
string
required
A Python expression string, e.g. "numpy.zeros(5).sum()". The expression is evaluated in the context of the already-imported modules in PID 1.
Response — 200 OK
{ "result": "0.0", "error": null, "exit_code": 0 }
result
string
String representation of the expression’s return value. null when the expression raised an exception.
error
string
Error message if the expression raised an exception. null on success.
exit_code
integer
required
0 on success, non-zero if the eval failed.

POST /v1/sandboxes/:id/branch

Pause a running sandbox, snapshot its in-flight memory and vCPU state into a new snapshot tag, then resume the sandbox. The resulting snapshot is completely independent of the source sandbox’s lifecycle — fork from it or delete it regardless of whether the source is still alive. Volumes inherited from the source snapshot are carried forward automatically, so grandchildren see the same persistent disks. This is forkd’s key primitive for mid-execution fan-out: an agent can reach a decision point, branch its entire VM state, and have multiple independent children each explore a different path — all inheriting the same prior computation cost-free. Path parameter
id
string
required
The ID of the running sandbox to branch.
Request body
tag
string
Optional tag for the new snapshot. When unset, the daemon auto-generates branch-<source-id>-<unix-ts>. Must match ^[A-Za-z0-9_][A-Za-z0-9._-]{0,63}$ when provided.
mode
string
default:"full"
v0.4+. Branch mode controlling the source VM’s pause window and the mechanism used to capture memory. One of "full", "diff", or "live".
diff
boolean
default:"false"
Legacy v0.3 compatibility. Equivalent to mode: "diff". Use mode instead for new code. Mutually exclusive with mode — sending both returns 400 Bad Request.
wait
boolean
default:"true"
v0.4+, only meaningful with mode: "live". When false, the daemon returns a SnapshotInfo with status: "writing" as soon as the source VM resumes (~10 ms). The background memory copy continues asynchronously and status flips to "ready" (or "failed") when complete. Poll GET /v1/snapshots to detect completion.When true (the default), the call blocks until the full copy finishes before returning status: "ready".Setting wait: false without mode: "live" returns 400 Bad Request.
Response — 201 Created Returns a SnapshotInfo with branched_from set to the source sandbox ID and pause_ms populated with the measured pause window.
{
  "tag": "checkpoint-1",
  "dir": "/var/lib/forkd/snapshots/checkpoint-1",
  "created_at_unix": 1717001000,
  "branched_from": "sb-67a1b3-0000",
  "pause_ms": 1820,
  "status": "ready"
}
With mode: "live" and wait: false, the response returns while the background copy is still running:
{
  "tag": "checkpoint-live",
  "dir": "/var/lib/forkd/snapshots/checkpoint-live",
  "created_at_unix": 1717001010,
  "branched_from": "sb-67a1b3-0000",
  "pause_ms": 47,
  "status": "writing"
}
Pause-window semantics by mode The source VM is paused at the vCPU level (kernel state and open TCP sockets are preserved; application-level keepalives may time out for "full" due to the longer pause window).
ModeSource pause p50Notes
"full"0.5–8 sWhole guest RAM written during pause. Highest pause cost; simplest restore. ~150–500 ms for 512 MiB on ext4 SSD.
"diff"~200 ms idleDirty-page diff during pause; full memory.bin reconstructed asynchronously. v0.3+.
"live"sub-50 msUFFD_WP: vmstate-only dump during pause, memory streamed asynchronously. v0.4+. 56 ms p50 / 64 ms p90 on 1.5 GiB source.
If resume fails after a successful snapshot, the snapshot file is intact and returned to the caller. The source sandbox may be left in an unknown state. The controller logs this as a warning rather than failing the request, because the caller’s primary expectation — a valid new snapshot — has been met.
Errors
StatusCause
400 Bad RequestBoth mode and diff were set, or wait: false was sent without mode: "live".
404 Not FoundSource sandbox ID is not in the live VM registry.
409 ConflictA snapshot with the requested tag already exists on disk — DELETE it first.
409 ConflictA branch for this exact tag is already in flight.
503 Service UnavailableDaemon is at its branch concurrency cap (default 4). Retry after a short delay.
500 Internal Server ErrorPause, snapshot, or resume failure — check daemon logs.
curl example
TOKEN=$(cat /etc/forkd/token)
SB_ID="sb-67a1b3-0000"

# Full branch (safest, highest pause cost)
curl -s \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "http://127.0.0.1:8889/v1/sandboxes/${SB_ID}/branch" \
  -d '{
    "tag": "checkpoint-1",
    "mode": "full"
  }'

# Live branch — source pauses sub-50 ms, returns immediately with status=writing
curl -s \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "http://127.0.0.1:8889/v1/sandboxes/${SB_ID}/branch" \
  -d '{
    "tag": "checkpoint-live",
    "mode": "live",
    "wait": false
  }'
# Poll until status == "ready"
curl -s \
  -H "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:8889/v1/snapshots | jq '.[] | select(.tag=="checkpoint-live") | .status'

Schemas

SandboxInfo

Returned by POST /v1/sandboxes, GET /v1/sandboxes, and GET /v1/sandboxes/:id.
{
  "id": "sb-67a1b3-0000",
  "snapshot_tag": "py",
  "netns": "forkd-child-1",
  "guest_addr": "10.42.0.2:8888",
  "created_at_unix": 1717000123,
  "pid": 314159,
  "memory_limit_mib": 256
}
id
string
required
Unique sandbox identifier generated by the daemon, e.g. sb-67a1b3-0000. Use this in all subsequent per-sandbox API calls.
snapshot_tag
string
required
The snapshot tag this sandbox was forked from.
netns
string
Name of the network namespace this sandbox is placed in, e.g. forkd-child-1. null or absent when per_child_netns was false at fork time.
guest_addr
string
required
The in-guest TCP address of the guest agent, in host:port form (e.g. 10.42.0.2:8888). The Python and TypeScript SDKs connect here for exec and eval.
created_at_unix
integer
required
Unix timestamp (seconds) when this sandbox was forked.
pid
integer
Host-side PID of the Firecracker process for this sandbox. Useful for attaching debuggers or verifying the process is still alive. May be absent if the daemon hasn’t yet recorded it.
memory_limit_mib
integer
cgroup v2 memory.max limit in MiB, if one was set at fork time. Absent when no limit was requested.

SnapshotInfo from branch

See SnapshotInfo in the Snapshots reference for the full field descriptions. When returned by POST /v1/sandboxes/:id/branch, the following fields are always populated in addition to the base fields:
branched_from
string
required
The source sandbox ID that was paused to produce this snapshot.
pause_ms
integer
required
Measured pause window in milliseconds.
status
string
required
"ready" for synchronous branch modes (full, diff, and live with wait: true). "writing" for mode: "live" with wait: false until the background copy completes.

Build docs developers (and LLMs) love