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.

Once a parent snapshot exists, these commands control the sandbox lifecycle — from forking hundreds of children in parallel, to inspecting live sandboxes, to diagnosing the host, to benchmarking real latency on your hardware.

forkd fork

Fork N children from a tagged snapshot in parallel. Each child is a separate Firecracker process that mmaps the parent’s memory.bin with MAP_PRIVATE; the kernel implements copy-on-write at the page level so children share the parent’s resident memory until they diverge. After all restores fire, the command waits --settle-secs seconds, counts alive children, shuts them down, and removes the work directory (unless --keep-workdir is set).
--tag
string
required
Snapshot tag to fork from. The snapshot must exist at $XDG_DATA_HOME/forkd/snapshots/<tag>/vmstate.
--n / -n
usize
required
Number of children to fork.
--settle-secs
u64
default:"2"
Seconds to let children run before reporting their alive count and shutting down.
--per-child-netns
boolean
Spawn each child inside its own forkd-child-<i> network namespace. Required for fanout greater than 1 when all children share the same guest IP (10.42.0.2). Provision namespaces first with sudo scripts/netns-setup.sh N. Requires root or CAP_SYS_ADMIN.
--memory-limit-mib
u64
Optional cgroup v2 memory.max limit per child in MiB. Children exceeding this are OOM-killed by the kernel. Requires root or a delegated cgroup (/sys/fs/cgroup/cgroup.controllers must be present). See crates/forkd-vmm/src/cgroup.rs for implementation details.
--live-fork
boolean
Back each child’s RAM with a memfd shared region (MFD_SHARED) instead of a private file copy. Required if you later want to take a v0.4 live BRANCH off this child — mode: "live" arms UFFD_WP on the memfd, which only works on shmem-backed VMAs. Requires Linux ≥ 5.7, the vendored Firecracker fork, and vm.unprivileged_userfaultfd=1. No cost at spawn time beyond the backend swap; cost shows up on the first live BRANCH. forkd doctor probes both prerequisites.
--hugepages
boolean
Back the memfd with 2 MiB hugepages (MFD_HUGETLB | MFD_HUGE_2MB). Only meaningful with --live-fork. Reduces TLB pressure during spawn-many and live BRANCH bulk-copy. Requires hugepages to be reserved on the host (echo N > /proc/sys/vm/nr_hugepages). forkd doctor checks availability. Falls back to normal pages with a warning if the pool is exhausted. Requires --live-fork.
--keep-workdir
boolean
Keep /tmp/forkd-fork-<tag>/ after shutdown (default: removed). Contains child console logs and Firecracker API sockets — useful for post-mortem inspection.
Examples:
# Fork 10 children (requires per-child netns provisioned)
sudo -E forkd fork --tag pyagent -n 10 --per-child-netns

# Fork 100 children with a 256 MiB memory cap each
sudo -E forkd fork --tag pyagent -n 100 --per-child-netns --memory-limit-mib 256

# Fork with live-fork enabled for future BRANCH support
sudo -E forkd fork --tag pyagent -n 5 --per-child-netns --live-fork

# Fork with hugepages for reduced TLB pressure
sudo -E forkd fork --tag pyagent -n 20 --per-child-netns --live-fork --hugepages

# Fork and keep work directories for debugging
sudo -E forkd fork --tag pyagent -n 3 --per-child-netns --keep-workdir

forkd run

One-shot sandbox: build rootfs from a Docker image (if not cached), take a one-off snapshot, fork one child, exec the command via the guest agent, print stdout/stderr, and shut down. The exit code of the command is propagated as the process exit code. Equivalent to: build rootfs → snapshot → fork 1 → exec command → shutdown. Use for ad-hoc “give me a quick isolated sandbox” invocations; use forkd fork for high-throughput fan-out.
--image
string
required
Docker image to run (e.g. python:3.12-slim, ubuntu:24.04).
--extra
string[]
Extra apt packages to bake into the rootfs. Repeatable.
--cache
path
default:"/var/cache/forkd"
Rootfs cache directory. Re-running with the same image skips the Docker → ext4 step. Also read from FORKD_RUN_CACHE.
--kernel
path
required
Kernel image path. Also read from FORKD_KERNEL.
--tap
string
default:"forkd-tap0"
Host tap device. Also read from FORKD_TAP.
cmd
string[]
Command and arguments to execute inside the sandbox, passed after --.
Examples:
# Run a one-off Python expression
sudo -E forkd run --image python:3.12-slim --kernel ./vmlinux-6.1.141 \
    -- python3 -c "import numpy; print(numpy.zeros(5).sum())"
# 0.0

# Run a shell command
sudo -E forkd run --image ubuntu:24.04 --kernel ./vmlinux-6.1.141 \
    -- bash -c "uname -r && whoami"

forkd parent build

Convert a Docker image into a writable ext4 rootfs image with /forkd-init.sh and /forkd-agent.py preinstalled. This is the first step of the manual snapshot pipeline. Calls scripts/build-rootfs.sh internally (must be on $FORKD_SCRIPTS_DIR or discoverable by walking up from the binary). Subcommand: forkd parent build <image> [flags]
image
string
required
Docker image reference (positional argument). Examples: python:3.12-slim, ubuntu:24.04.
--output
path
Output .ext4 file path. Default: ./<image-slug>.ext4 in the current directory.
--size-mib
u32
default:"1536"
Image size in MiB.
--extra
string[]
Extra apt packages to install on top of the base image. Repeatable.
Examples:
# Build a Python + numpy rootfs
sudo forkd parent build python:3.12-slim --extra python3-numpy

# Build with custom output path and size
sudo forkd parent build python:3.12-slim \
    --output /var/lib/forkd/rootfs/myagent.ext4 \
    --size-mib 2048 \
    --extra "python3-numpy python3-scipy python3-matplotlib"

# Then snapshot the result
sudo forkd snapshot --tag myagent \
    --kernel ./vmlinux-6.1.141 \
    --rootfs ./python-3-12-slim.ext4 \
    --tap forkd-tap0

forkd ls

List all live sandboxes tracked by the daemon by calling GET /v1/sandboxes. Prints a table of sandbox IDs, statuses, tags, PIDs, and network addresses. Equivalent to querying the REST API directly.
--daemon-url
string
default:"http://127.0.0.1:8889"
Controller daemon base URL. Also read from FORKD_URL.
--daemon-token
string
Bearer token for the controller daemon. Also read from FORKD_TOKEN.
Example:
forkd ls

# ID                   STATUS   TAG       PID     NETNS
# sb-67a1b3-0000       running  pyagent   84123   forkd-child-1
# sb-67a1b3-0001       running  pyagent   84201   forkd-child-2

forkd kill

Kill one or more live sandboxes via DELETE /v1/sandboxes/:id. Supports killing by explicit ID list, by snapshot tag, or all sandboxes at once.
ids
string[]
Sandbox IDs to kill (positional, variadic). Ignored when --all or --tag is set.
--all
boolean
Kill every live sandbox the daemon knows about. Mutually exclusive with --tag.
--tag
string
Kill every sandbox forked from this snapshot tag. Mutually exclusive with --all.
--daemon-url
string
default:"http://127.0.0.1:8889"
Controller daemon base URL. Also read from FORKD_URL.
--daemon-token
string
Bearer token. Also read from FORKD_TOKEN.
Examples:
# Kill a single sandbox
forkd kill sb-abc-0000

# Kill multiple sandboxes
forkd kill sb-abc-0000 sb-abc-0001

# Kill all sandboxes from a specific tag
forkd kill --tag pyagent

# Kill every sandbox the daemon knows about
forkd kill --all

forkd cleanup

Remove orphaned /tmp/forkd-{fork,parent,unpack,pull}-* work directories and temp files left behind by forkd runs that crashed or were killed before they could clean up. Dry-run by default — lists candidates without deleting. Pass --yes to actually delete. Sweeps the following prefixes under /tmp/:
  • forkd-fork-* — child work directories from forkd fork
  • forkd-parent-* — parent work directories from forkd snapshot
  • forkd-unpack-* — scratch directories from forkd unpack
  • forkd-pull-* — temp pack files from forkd pull
Any directory with a live Firecracker process holding an open file descriptor inside it is skipped automatically (detection uses /proc/*/fd/ symlink scanning, not lsof).
--yes
boolean
Actually delete the directories marked DEL (default: list only / dry run). Short form: -y.
Examples:
# Dry run — see what would be removed
forkd cleanup

# Actually remove orphaned directories
forkd cleanup --yes

forkd doctor

Diagnose the host setup. Runs 17 checks and emits fix hints for each non-passing check. Safe to run unprivileged — checks that require root are skipped with a note rather than failing. Run this first after a fresh scripts/setup-host.sh, whenever forkd fork fails mysteriously, or after a kernel upgrade. The 17 checks, in order:
CheckWhat it tests
platformReads /proc/version; requires Linux
hw virtChecks /proc/cpuinfo for vmx (Intel VT-x) or svm (AMD-V) flags
kvmChecks /dev/kvm exists and is readable by the current user
cgroup v2Checks /sys/fs/cgroup/cgroup.controllers for a unified cgroup v2 hierarchy
ip_forwardReads /proc/sys/net/ipv4/ip_forward; must be 1
tap deviceChecks /sys/class/net/forkd-tap0 for admin-UP state
per-child netnsCounts forkd-child-* entries under /var/run/netns
firecrackerChecks firecracker binary is on $PATH
firecracker versionParses firecracker --version; warns below 1.10, fails below 1.5
kernel imageSearches ./vmlinux-6.1.141, ./vmlinux, /var/lib/forkd/kernels/vmlinux, /usr/local/share/forkd/vmlinux
snapshot dirChecks whether $XDG_DATA_HOME/forkd/snapshots/ exists and counts snapshots
snapshot dir spaceCalls statvfs on the snapshot filesystem; warns below 5 GiB, fails below 1 GiB
dockerRuns docker info; warns (not fails) if missing — only needed for from-image / parent build
daemonGET /v1/snapshots against --daemon-url; warns if unreachable, fails on HTTP 401
uffd_wp (v0.4 live BRANCH)Probes UFFD_FEATURE_WP_ASYNC via forkd_uffd::probe; warns if unavailable (Diff/Full still work)
memfd_create (v0.4 live BRANCH)Probes memfd_create syscall availability; warns if unavailable
hugepagesReads /proc/meminfo HugePages_Total and HugePages_Free; warns if pool is empty or exhausted
Each check returns one of: ✓ PASS (green), ⚠ WARN (yellow, non-blocking), ✗ FAIL (red, blocking), or · SKIP (grey, not applicable on this platform). The command exits with a non-zero status if any check fails.
--daemon-url
string
default:"http://127.0.0.1:8889"
Controller daemon base URL for the daemon-reachable check. Also read from FORKD_URL.
--daemon-token
string
Bearer token for the daemon check. Also read from FORKD_TOKEN.
Examples:
# Run all checks
forkd doctor

# Point at a non-default daemon URL
forkd doctor --daemon-url http://10.0.0.5:8889 --daemon-token "$TOKEN"

# Sample passing output (17 checks, Linux 6.14):
#   ✓  platform            Linux version 6.14.0 ...
#   ✓  hw virt             vmx (CPU supports virtualization)
#   ✓  kvm                 /dev/kvm OK
#   ✓  cgroup v2           cpuset cpu io memory hugetlb pids rdma misc
#   ✓  ip_forward          1 (forwarding enabled)
#   ✓  tap device          forkd-tap0 (admin-UP, idle)
#   ✓  per-child netns     100 provisioned
#   ✓  firecracker         /usr/local/bin/firecracker
#   ✓  firecracker version v1.12.0
#   ✓  kernel image        /var/lib/forkd/kernels/vmlinux
#   ✓  snapshot dir        /home/user/.local/share/forkd/snapshots (3 snapshots)
#   ✓  snapshot dir space  42.3 GiB free at /home/user/.local/share/forkd/snapshots
#   ✓  docker              daemon reachable
#   ✓  daemon              http://127.0.0.1:8889 responding
#   ✓  uffd_wp (v0.4 live BRANCH)    supported
#   ✓  memfd_create (v0.4 live BRANCH)  supported
#   ✓  hugepages           512/512 2 MiB pages free (1024 MiB available)
#
#   pass=17  warn=0  fail=0  skip=0

forkd bench

Quick latency probe against a live daemon. Runs a representative spawn → exec → branch (diff) → fanout → cleanup cycle and prints per-step timing in a screenshot-friendly format. Use this to answer “is forkd actually fast on this box?”, for regression checks after a config change, or to validate that benchmark numbers from the README reproduce on your hardware. Steps performed:
  1. Spawn 1 source sandbox (POST /v1/sandboxes n=1).
  2. Exec sh -c echo via POST /v1/sandboxes/:id/exec.
  3. Diff BRANCH the source (POST /v1/sandboxes/:id/branch with diff: true).
  4. Spawn N grandchildren from the branch tag with per_child_netns.
  5. Kill all children and the source, measure cleanup time.
  6. Print per-step wall-clock ms and a total.
--tag
string
Snapshot tag to spawn from. Defaults to the first snapshot the daemon reports via GET /v1/snapshots.
--n
usize
default:"5"
Fanout: how many grandchildren to spawn from the branch in step 4.
--per-child-netns
boolean
default:"true"
Whether to use per-child network namespaces for the fanout. Defaults to true — the fanout step will fail without per-child netns when n > 1 and all children share the same guest IP.
--daemon-url
string
default:"http://127.0.0.1:8889"
Controller daemon base URL. Also read from FORKD_URL.
--daemon-token
string
Bearer token for the controller daemon. Also read from FORKD_TOKEN.
Example output (from the README, i7-12700, Ubuntu 24.04, Linux 6.14):
forkd bench against snapshot py-numpy
  fanout n=5 per_child_netns=true

  spawn (n=1)              61 ms  sb-...-0027
  exec round-trip          22 ms  exit=0
  branch (diff=true)      287 ms  pause_ms=234 diff_physical_bytes=393216
  fanout (n=5)             65 ms  13ms/child
  cleanup                 136 ms  6 sandboxes
                          -----
  total                   571 ms
# Bench against the first available snapshot
forkd bench

# Bench against a specific tag with a larger fanout
forkd bench --tag py-numpy --n 20

# Bench without per-child netns (single child fanout only)
forkd bench --tag py-numpy --n 1 --per-child-netns false

forkd workspace

Stateful long-lived sandboxes that survive suspend/resume across daemon restarts. A workspace tracks a live sandbox by name. When suspended, the sandbox’s current state is snapshotted to a ws-<name>-state tag and the sandbox is killed. When resumed, the daemon restores from that state tag. The source snapshot (--snapshot) is never modified. All workspace subcommands communicate with the daemon via FORKD_URL / FORKD_TOKEN.

forkd workspace create

Create a new workspace by spawning a sandbox from a snapshot tag.
name
string
required
Workspace name. 1–64 chars, ASCII alphanumeric, dash, or underscore (positional argument).
--snapshot
string
required
Snapshot tag to fork from.
--per-child-netns
boolean
Place the live sandbox in its own pre-provisioned netns.
--memory-limit-mib
u64
Cgroup memory.max for the live sandbox in MiB.
--daemon-url
string
default:"http://127.0.0.1:8889"
Controller URL. Also read from FORKD_URL.
--daemon-token
string
Controller bearer token. Also read from FORKD_TOKEN.

forkd workspace suspend

Snapshot the workspace’s live sandbox and kill it. State is preserved under ws-<name>-state; a subsequent resume restores from there.
name
string
required
Workspace name (positional argument).
--diff
boolean
Use v0.3 Diff snapshot mode for the suspend write. ~200 ms source pause vs seconds for Full.
--daemon-url
string
default:"http://127.0.0.1:8889"
Also read from FORKD_URL.
--daemon-token
string
Also read from FORKD_TOKEN.

forkd workspace resume

Restore the workspace from its suspended state snapshot.
name
string
required
Workspace name (positional argument).
--daemon-url
string
default:"http://127.0.0.1:8889"
Also read from FORKD_URL.
--daemon-token
string
Also read from FORKD_TOKEN.

forkd workspace list

List all workspaces tracked by the daemon. Output columns: NAME, STATUS, source=<snapshot tag>, state=<state tag>, live=<sandbox id>.
--daemon-url
string
default:"http://127.0.0.1:8889"
Also read from FORKD_URL.
--daemon-token
string
Also read from FORKD_TOKEN.

forkd workspace delete

Delete a workspace. Kills the live sandbox if any and removes the state snapshot. Does not touch the source snapshot.
name
string
required
Workspace name (positional argument).
--daemon-url
string
default:"http://127.0.0.1:8889"
Also read from FORKD_URL.
--daemon-token
string
Also read from FORKD_TOKEN.
Full workspace lifecycle example:
# Create a workspace from the py-numpy snapshot
forkd workspace create myagent --snapshot py-numpy --per-child-netns

# Drive the sandbox (exec commands, eval expressions, etc.)
forkd exec --child forkd-child-1 -- python3 -c "print('hello from workspace')"

# Suspend (snapshots current state, kills sandbox)
forkd workspace suspend myagent --diff

# List all workspaces
forkd workspace list
# myagent   suspended  source=py-numpy   state=ws-myagent-state  live=-

# Resume from suspended state
forkd workspace resume myagent

# Delete the workspace when done
forkd workspace delete myagent

forkd where

Print the snapshot data directory path and exit. No flags.
forkd where
# /home/user/.local/share/forkd

# Use in scripts
SNAP_DIR="$(forkd where)/snapshots/pyagent"
ls "$SNAP_DIR"
# memory.bin  vmstate  snapshot.json

forkd wp-bench

Exercise the UFFD_WP machinery on a synthetic memfd outside of the Firecracker integration. Creates a memfd of the requested size, populates it with a known pattern, arms UFFDIO_WRITEPROTECT, runs the bulk-copy and fault-handler pair, finalizes, and prints timing. Use this to benchmark arm_duration and bulk_copy_clean throughput on a given kernel and filesystem before committing to the full live-BRANCH integration. Requires Linux x86_64, kernel ≥ 5.7, and either root or sysctl vm.unprivileged_userfaultfd=1.
--region-mib
u64
default:"64"
Size of the memfd region in MiB.
--snapshot
path
default:"/tmp/forkd-wp-bench.snapshot"
Path to write the snapshot file. Removed after the run by default.
Examples:
# Run with default 64 MiB region
sudo forkd wp-bench

# Benchmark with a larger region
sudo forkd wp-bench --region-mib 512

# Write snapshot to a custom path
sudo forkd wp-bench --region-mib 256 --snapshot /tmp/my-wp-bench.snapshot

Build docs developers (and LLMs) love