MicroVMs for agents, designed for home or at scale.
Give an agent a shell and it will install packages, start servers, run builds and, sooner or later, break something. Shards runs your agents in microVMs: small virtual machines with their own Linux kernel, behind your processor's virtualization, so what runs inside one sees its own machine, not yours.
A microVM is made from an image, a Dockerfile or an Agentfile, and it is the OS your agents run on. Many agents share one microVM, each in a domain of its own: its own processes, files, networks, devices and permissions, and nothing else unless you grant it. A whole microVM, agents and all, is saved and restored in microseconds. Your agents can:
- Start in under 200 µs
- Share a microVM's OS, each in its own domain
- Get a microVM to themselves when they shouldn't share one
- Reach only the networks, files and devices you give them
- Use the images, Dockerfiles, Compose files and
dockercommand lines you already have - Use GPUs
- Break anything in their microVM without touching the rest of your computer
Shards never needs root, and it is one file.
$ time shards run vm --kernel vmlinux --init shards-init --cmdline "console=ttyS0 quiet"
shards-init: pid 1 running at uptime 0.012768s
[ 0.015804] reboot: Power down
real 0m0.028s
user 0m0.019s
sys 0m0.007s
$ time shards restore vm snap
SHARDS-TEST PASS
[ 0.016121] reboot: Power down
real 0m0.007s
user 0m0.002s
sys 0m0.003sBooting Linux, running a program and shutting down took 28 ms. Restoring a saved microVM took 7 ms, most of it process start and exit. The saved microVM runs the test program from Snapshots.
Note
Output captured on an Apple M5 Max with macOS 26.4.1, from a release build at e8ec2e6,
with Firecracker's CI build of Linux 6.18 as vmlinux.
Important
Shards is young, at 0.x. See Where things stand for what works on which machines.
Download shards for your machine from the
latest release, and check it
against the release's SHA256SUMS (sha256sum -c on Linux):
os=$(uname -s | tr A-Z a-z); arch=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
base=https://github.com/hyper-light/shards/releases/latest/download
curl -fL -O $base/shards-$os-$arch -O $base/SHA256SUMS
grep " shards-$os-$arch\$" SHA256SUMS | shasum -a 256 -c
chmod +x shards-$os-$arch && mv shards-$os-$arch ~/.local/bin/shardsOn Linux, shards-linux-amd64-musl and shards-linux-arm64-musl are static, for any
distribution; on Windows, download shards-windows-amd64.exe. Or build it from source with
Rust 1.98 (pinned by the toolchain file):
git clone https://github.com/hyper-light/shards && cd shards
cargo build --release -p shards
cp target/release/shards ~/.local/bin/shards is the only file there is. The programs it runs each microVM and its networks with
are inside it; it writes them to your cache directory the first time it needs them.
- macOS: 15 or later, on Apple silicon.
- Linux: x86_64 or arm64, with access to
/dev/kvm(usually thekvmgroup). Add--target x86_64-unknown-linux-musl(oraarch64-unknown-linux-musl) for a static binary. - Windows: x86_64, with the Windows Hypervisor Platform turned on. On arm64 Windows, shards runs no microVMs yet.
Run a command in a new microVM made from an image:
$ shards run vm alpine echo hello
helloThe first run downloads shards' Linux kernel (19 MB on arm64, 28 MB on x86_64), checks it
against the SHA-256 built into shards, and keeps it. Later runs don't need the network for it.
To download it from somewhere else, such as a mirror, set SHARDS_KERNEL_URL; the checksum
still applies. The program each microVM runs first, shards-init, is part of shards.
On a terminal, shards draws its own pages. Piped, a docker command line prints what
docker prints, so scripts written for it keep working.
Images come from any registry, with the logins docker login keeps and the certificates
Docker's certs.d holds. A registry on another machine that speaks plain HTTP is reached
once Docker's daemon.json lists it under insecure-registries, as Docker reaches one;
unlike Docker, shards still checks the certificate of one that answers in HTTPS, and never
sends your password in plain HTTP. Its registry-mirrors are asked first for what comes
from Docker Hub, each with its own login, never your Docker Hub one.
The docker CLI, docker compose, Docker's SDKs, Testcontainers and agent frameworks work
with shards unchanged: what they ask for as a container is a microVM, and each Compose
service is a microVM of its own. docker buildx build builds in a microVM, with a builder
of buildx's own on your host's network (docker buildx create --driver docker-container --driver-opt network=host). Point them at shards' Engine API, a file in shards' home that
only your user can reach (no network port):
$ shards run daemon --detached
$ export DOCKER_HOST="unix://$HOME/Library/Application Support/shards/engine.sock" # macOS
$ export DOCKER_HOST="unix://${XDG_DATA_HOME:-$HOME/.local/share}/shards/engine.sock" # Linux
$ docker run --rm alpine echo hello
helloOn Windows it is a named pipe only your user can open, named afresh by each daemon:
shards list contexts shows it as DOCKER_HOST takes it (npipe:////./pipe/...).
It is there while shards' daemon runs, which any shards command starts. The daemon stays
while a program keeps a connection open or asks something, and stops after
SHARDS_DAEMON_IDLE seconds without either (default 900). Served: microVMs (create, start,
attach, wait, logs, exec, copying files in and out, export, changes, processes, stats,
pause, restart, rename, update, stop, kill, remove, prune, list, inspect), events, images
(pull, push, list, inspect, tag, history, remove, prune, save, load, import), an image of
what a microVM changed, builds of a context sent with the call (each RUN step in a
microVM), volumes, networks, registry logins, searches (Docker Hub, quay.io and any
registry's catalog), disk use, version and system information. Disk use counts the saved
microVMs that make starts fast, too. Copies are packed and unpacked inside the microVM, and
land where its program will find them: in its volumes, running or stopped, and its tmpfs
mounts while it runs. A network's microVMs reach each other by name and nothing else through
it. A network can let them reach chosen hosts outside, and only those: made with --opt shards.egress=api.anthropic.com:443,github.com:443, its microVMs reach each host on its
port, look up those names and no others, and reach nothing else past themselves; a
network made without it lets nothing out. Stats add what the microVM itself costs your machine (microvm: its memory and CPU
time). A setting a microVM can't take is refused with the reason, never dropped. A microVM
refused a process past its --pids-limit says so in its events (pids-limit, with the
limit and how many it refused), as one that runs out of memory says oom.
shards run vm --use-api-socket gives the program it runs an engine of its own microVM's:
DOCKER_HOST points at a socket in its own files (/run/shards/engine.sock), so the
docker CLI, Testcontainers and CI tools inside it run what they ask for as containers in
that same microVM. What runs there takes nothing of your machine's: not its engine, not its files, not your
other microVMs, and your registry logins stay outside it. Each is on its own, like each
agent in the microVM: the program reaches it on the ports it publishes (-p 0:5432, at
localhost), and on the default network it reaches no other of them, nor any agent. On a
network made in the microVM (docker network create, a Compose project's networks),
they reach one another by name, each on the ports it declares (EXPOSE, expose:,
--expose and the ports it publishes) and on no other. None gets out of the microVM
farther than the program can, and none on an internal network gets out at all. One can
share another's network (--network container:db, Compose's network_mode: service:db)
as that one has it, and no more. Two microVMs' programs can each run one called
db; on your machine they're listed as MICROVM.db. What a microVM's engine made stops
when its program does, and is removed with it. docker run --use-api-socket through
shards' Engine API does the same, and the credentials the docker CLI would copy in are
refused (it warns). Its program can run what it asks for (docker run, attached or not),
exec in them, stop, kill, wait for, read the logs of, list, inspect and remove them, make
networks and volumes for them, connect and disconnect them as they run, and pull images:
shards pulls with the registry logins of whoever ran the microVM, and the image is then
that microVM's own; images of yours it never pulled aren't there for it. A bind (-v /work:/src) is of the microVM's own files, its volumes included, as a bind on any engine
is of that engine's machine's. A volume it makes (-v data:/var/lib/data, Compose's
volumes:) is the microVM's own: kept on your disk, where your machine lists it as
MICROVM.data, reached by no name of yours or of another microVM's, and removed with the
microVM. A call it can't serve yet is refused with what it serves.
On Linux, shards run vm --cgroup-parent agents.slice IMAGE (and Compose's cgroup_parent:, through
shards' Engine API file) puts a microVM under a slice of your own systemd manager, so
whatever you give that slice covers every microVM in it: systemctl --user set-property agents.slice MemoryMax=16G caps them together, and systemctl --user status agents.slice
shows what they use. Each microVM's memory, its network's process included, is counted
there from its first page, warm starts too, and only the first run under a slice asks
systemd for anything. It works from an ssh login as from a desktop session, with nothing to
set up. A run that shares another's network runs in that one's microVM, so it names
that one's slice or none.
Where no systemd manager of yours runs (a container, or a host on OpenRC, runit or s6) but
a cgroup has been delegated to you, name it by its path, as /proc/self/cgroup shows one:
shards run vm --cgroup-parent /agents IMAGE. Its microVMs are made in a cgroup of
their own inside it, counted there from their first page, and held to whatever limits you
write to /sys/fs/cgroup/agents. shards' daemon must be running inside the same delegated
tree, as the kernel lets you move your processes only within it; a path you may not use is
refused with why before anything starts.
On macOS, which has no such group, shards run vm --cgroup-parent agents IMAGE puts a
microVM in a group shards keeps for you, a folder of its home, groups/agents, with the
files a Linux cgroup has. Write 4G to its memory.high and its microVMs are held to it
together: past it, each gives back what it holds cold. Past its memory.max, with nothing
more to give back, the one that holds the most is stopped, as a cgroup's out-of-memory
killer stops one, and inspect says so. memory.current shows what they hold.
On Windows the same folder's microVMs run in one job object of the group's, whose memory
limit is its memory.max: together they never commit past it. On both, cpu.max (a quota
and its period in microseconds, as a cgroup's) holds the group's microVMs to that much CPU
together.
You need a kernel and a program to run as PID 1. Download shards' kernel, built reproducibly by CI (resources/kernel):
arch=$(uname -m | sed s/arm64/aarch64/)
file=$([ $arch = x86_64 ] && echo vmlinux || echo Image)-6.18.48-$arch
curl -fLo vmlinux https://github.com/hyper-light/shards/releases/download/kernel-6.18.48-17c3ba205400/$file
shasum -a 256 vmlinux| Architecture | SHA-256 |
|---|---|
| aarch64 (arm64) | 88fe0db804983c20158cd2fecdd5c6f90655ad5f33fa3f0cd0b22bf8c841391e |
| x86_64 | 85682e108bca1e3fd0d90ee649a939e344f507af703aacca1b266511f1052c08 |
Build shards-init, a tiny PID 1 that reports Linux's boot time and powers off:
cargo build -p shards-init --profile guest --target $arch-unknown-linux-musl
cp target/$arch-unknown-linux-musl/guest/shards-init .Boot it:
$ shards run vm --kernel vmlinux --init shards-init --cpus 4 --memory 512 --cmdline "console=ttyS0 quiet"
shards-init: pid 1 running at uptime 0.014162s
[ 0.018563] reboot: Power downThe console is your terminal, and Ctrl-A x stops the microVM. Drop quiet to see the
kernel log. --init takes any static Linux binary, --initrd a whole initramfs, and
--disk FILE[:ro] adds /dev/vda, /dev/vdb and so on. The defaults are 1 CPU and 256 MiB.
Set a microVM up once, save it, and start copies. A program inside the microVM decides when to save. The test program does it right away:
cargo build -p shards-testguest --profile guest --target $arch-unknown-linux-musl
cp target/$arch-unknown-linux-musl/guest/shards-testguest .$ shards run vm --kernel vmlinux --init shards-testguest --snapshot-dir snap \
--cmdline "console=ttyS0 quiet shards_test=resume"
$ du -h snap/*
43M snap/memory
20K snap/stateEmpty memory isn't stored, so 256 MiB of RAM takes 43 MiB on disk. shards restore vm snap
starts a copy exactly where the original asked to be saved, as in the example at the top.
- Copies are cheap. They share the snapshot's memory until they write to it.
- Copies don't collide. Linux reseeds its randomness in each copy as it wakes, so keys and IDs differ between copies. Values a program drew before the snapshot don't.
- Clocks keep running from the moment of the snapshot. Wall-clock time lags until something in the microVM sets it.
--hold preloads a copy and waits. A line on stdin starts it in 150 µs:
$ (sleep 1; echo) | shards restore vm snap --hold
shards-ready
SHARDS-TEST PASS
[ 0.016274] reboot: Power downA running microVM made from an image is saved whole with shards save vm NAME: its memory,
every process, terminal and open file in it, as they are. shards restore vm NAME resumes
it where it was, at the same address on its network.
shards move vm NAME CONTEXT moves a running microVM to the machine a context reaches,
saved here and resumed there where it was, its image, files and volumes with it; with
--live it keeps running while its memory is copied, and stops only for the last pass.
Note
Snapshots work on macOS, on x86_64 and arm64 Linux, and on x86_64 Windows. Live moves work on macOS and x86_64 Linux.
| Today (2026-11-04) | |
|---|---|
| MicroVMs on Apple silicon Macs, x86_64 and arm64 Linux, and x86_64 Windows | Work: booted directly and made from images, repeated runs served from warm copies |
| MicroVMs on arm64 Windows | Not yet: no hypervisor backend |
| Snapshots, and saving a running microVM whole | Every host above |
| Moving a running microVM to another machine | Saved and resumed there, on every host; live on macOS and x86_64 Linux |
| Disks, folders and volumes plugged into a running microVM, disks grown | macOS and Linux; a host's PCI device plugged in on Linux, tested nested with a test device |
| Images, Dockerfiles and Agentfiles | Pulled, pushed and built, every RUN step in a microVM of the build's own, for other CPUs too under emulation |
| Many agents in one microVM, each in its own domain | Works, from an Agentfile: each agent's own processes, files, networks and devices, and nothing it isn't granted |
| Networks, volumes and devices | Work: a microVM reaches only what you give it, networks deny by default, over IPv4 and IPv6 |
| GPUs | macOS, Linux (glibc and musl) and Windows (x86_64): your machine's GPU, for Vulkan in the microVM (--gpus all), tested on Apple's, NVIDIA's, AMD's and Intel's |
| PCI passthrough | Linux passes a host's function through with VFIO, tested with a test device. macOS's Hypervisor.framework provides no device assignment API. The Windows hosts tested here refuse device assignment and shards reports their answers; DDA on a Windows Server host that permits it remains unimplemented and needs a real hardware E2E (D154) |
docker command lines, Compose and the Docker Engine API |
Work, through shards' Engine API file |
| Running shards' commands on another machine | Works: shards create context, over QUIC |
The target is a usable microVM within 5 ms of the request, using less memory than Firecracker. The plan and its evidence are in docs/design/architecture.md.
Every command is an action and what it acts on: shards ACTION THING [ARG...].
shards ACTION alone lists the ways it is said, and shards --help lists every action.
Said the other way round, shards vm ls is shards list vm and shards image rm is
shards remove image. Where a command takes a microVM's name, the name of an image reaches
the microVMs running from it: shards stop vm ubuntu stops each.
| Command | What it does |
|---|---|
shards run vm IMAGE [COMMAND] |
Run a command in a new microVM made from IMAGE, pulled first if it isn't here. With -it, your terminal is the command's: ctrl-p ctrl-q leaves it running. Repeated runs of an image are served from copies of its booted microVM that the daemon restores ahead of time |
shards run container IMAGE [COMMAND] |
The same, IMAGE a container image: one that is a shards microVM already is refused, and run vm runs it |
shards create vm IMAGE [COMMAND] |
Make a microVM without starting it |
shards attach vm NAME |
Join your terminal to a running microVM's command; ctrl-p ctrl-q leaves it running |
shards start vm NAME · shards restart vm NAME |
Start a stopped microVM again, its files as it left them; stop one and start it again |
shards exec vm NAME COMMAND |
Run a command in a running microVM |
shards stop vm NAME · shards kill vm NAME |
Stop a microVM: its stop signal, then SIGKILL once its grace is up; or kill it |
shards pause vm NAME · shards unpause vm NAME |
Freeze a microVM where it is, every vCPU and device, at no CPU; let it run on |
shards wait vm NAME |
Wait for a microVM to stop, and print its command's exit status |
shards update vm NAME --cpus N --memory SIZE ... |
Change what a running microVM may take of your machine, as it runs |
shards plug disk NAME FILE:PATH[:ro] [--size SIZE] · shards plug volume NAME SOURCE:PATH[:ro] · shards unplug disk|volume NAME PATH |
Plug a disk, a folder of yours or a volume into a running microVM, mounted at PATH; --size makes FILE an empty ext4 disk where it isn't there. Unplug it once nothing there uses it |
shards grow disk NAME PATH SIZE |
Grow a plugged disk, and its file system, as the microVM runs |
shards plug function NAME FUNCTION · shards unplug function NAME FUNCTION |
On Linux, give a running microVM's program one of your PCI devices (0000:01:00.0, bound to vfio-pci), through its IOMMU; the microVM is started with --device /dev/vfio/vfio |
shards move vm NAME CONTEXT |
Move a running microVM to the machine a context reaches, and resume it there where it was; --live keeps it running while it moves |
shards save vm NAME [SNAPSHOT] |
Save a running microVM whole, its memory, processes and devices as they are, then stop it (or --leave-running). Its volumes come with it |
shards restore vm NAME [SNAPSHOT] |
Resume a saved microVM exactly where it was, from its newest snapshot unless you name one |
shards list vm · shards list image · shards list volume |
List microVMs, images or volumes |
shards list snapshots NAME · shards remove snapshot NAME SNAPSHOT... |
List a microVM's snapshots, newest first, with when each was saved and the disk it takes; remove them |
shards inspect vm NAME · shards inspect image NAME |
What a microVM runs, or an image's documents, --platform's for an image of several |
shards inspect disk · shards inspect system · shards inspect guest |
What shards takes on disk; what it is, holds and runs on; the kernel and shards-init runs boot |
shards read logs NAME · shards watch logs NAME |
What a microVM's command printed; what it prints, as it prints it |
shards read processes NAME · shards watch processes NAME |
The processes a microVM's agents started and ended, each whole, as the kernel saw them; as they come |
shards read ports NAME |
Which of a microVM's agents each published port reaches; for what its own engine made, who reaches each port it declares or listens on, and what would declare one it listens on that none reaches |
shards read changes NAME |
What a microVM changed of its image's files |
shards watch vms [NAME...] · shards watch system |
What microVMs take of your machine, live; what happens to microVMs and images, as it happens |
shards copy vm NAME:PATH PATH · shards copy vm PATH NAME:PATH |
Copy files out of a microVM, or into one |
shards rename vm NAME NEW_NAME |
Name a microVM again |
shards remove vm NAME · shards prune vm |
Remove a microVM, and its snapshots with it; remove every stopped one |
shards create volume [NAME] · shards remove volume NAME · shards prune volume |
Make a volume microVMs mount; remove one no microVM mounts; remove those no microVM mounts |
shards pull image NAME · shards push image NAME |
Download an image and make it a microVM, for this machine's CPU or --platform's; upload one, or --platform's microVM of it alone. Every layer is checked against its digests before it is kept |
shards build image PATH |
Build an image from the Dockerfile or Agentfile in PATH, its RUN steps in a microVM of the build's own. --platform linux/amd64,linux/arm64 builds for both, a step for another CPU under that CPU's emulator inside the build's microVM |
shards build agent DIR · shards push agent NAME · shards sign agent NAME |
Build an agent (or a harness or an MCP server) as an OCI artifact, push it, and sign it with your key or your identity |
shards read history IMAGE · shards tag image SOURCE TARGET |
How an image's layers were made, --platform's for an image of several; name an image again |
shards save image NAME · shards load image · shards export vm NAME |
Images to a tar archive and back, every platform of each or --platform's alone; a microVM's files as a tar archive |
shards commit vm NAME [IMAGE] |
Make an image of what a microVM changed |
shards import vm FILE [NAME] · shards import image FILE|URL|- [NAME] |
Bring in a shards microVM saved whole (docker save shards.local/NAME), or make a microVM of a tarball of files or an image archive |
shards remove image NAME · shards prune image |
Remove an image with its stopped microVMs, or --platform's microVM of it alone, freeing the disk it took; remove unused images |
shards login registry [SERVER] · shards logout registry [SERVER] |
Log in to a registry, or forget a login: kept where docker login keeps one, so both find it |
shards search image TERM |
Find images on Docker Hub, on quay.io (quay.io/TERM), or in any registry's catalog (HOST/TERM); on a terminal, whether each runs on this machine, and what its first pull downloads |
shards run daemon · shards stop daemon |
The daemon that serves runs from warm microVMs, which the first run starts; end it and its runs. It keeps up to SHARDS_POOL microVMs ready for each image you run (default 2), and none once the image has gone unused for SHARDS_POOL_KEEP seconds (default 600) |
shards configure guest --kernel FILE --init FILE |
Choose the kernel and shards-init runs boot, in place of shards' own |
shards configure disk [--build-cache-max N%|SIZE] [--log-history SIZE] ... |
How much of your disk shards keeps: what the build cache always keeps, the most it keeps, and the disk it leaves free (--build-cache-reserve, --build-cache-max, --build-cache-free; 10%, 80% and 20% of the disk by default), and the most a run's log keeps of its newest output (--log-history, 1 GiB by default). A change takes at the next build or run. Alone, it shows each setting; --reset brings back the defaults |
shards run daemon --listen ADDR:PORT |
Let machines you invite run shards on this one, over QUIC on UDP ADDR:PORT: run it where the microVMs should run, such as a Linux machine with a GPU. Nothing is reachable until you invite a machine, and it reaches only what you could do here with shards |
shards invite client --name NAME --output FILE |
An invitation, a file only you can read, which lets one machine join this one as NAME, within an hour. shards list invitations, shards remove invitation NAME, shards list clients and shards remove client NAME show and withdraw them |
shards create context NAME --invite-file FILE |
Join the machine that invited you. Then shards --context NAME ACTION THING runs on that machine, its output and exit status yours, and shards use context NAME makes it the one every command runs on; default is this one. Its microVMs, images and volumes are that machine's; your logins, environment, terminal and files stay yours: builds send your context and only what changed since the last, your build's secrets cross in memory alone, its outputs come back to your files, --ssh reaches your agent here, copy vm copies to and from your machine (from Linux and macOS), and --cidfile is written here. Tools that speak the Engine API reach it too, through the Engine file shards inspect context NAME shows |
shards run vm --kernel FILE [options] |
Boot a kernel directly in a microVM of its own |
shards restore vm DIR [--hold] |
Start a copy of the microVM saved in DIR; --hold preloads it and waits for a line on stdin |
docker's command lines work too, as docker takes them (shards ps, shards logs -f web,
shards checkpoint create web first), for the scripts written for it.
Where Docker runs on your machine, every microVM shards makes (pulled, run, built,
committed, tagged, loaded or imported) shows in Docker's images as shards.local/NAME: its
disk, and the config of the image it was made of; shards remove image NAME takes it from
there too, and docker save shards.local/NAME | shards import vm - brings one back.
SHARDS_LOCAL_STORE=none keeps them out.
Option of run vm --kernel |
What it does |
|---|---|
--init FILE |
The static Linux binary to run as PID 1 |
--initrd FILE |
An initramfs, instead of --init |
--cmdline TEXT |
The kernel command line (default: console=ttyS0 earlycon panic=-1) |
--cpus N · --memory MIB |
Size (default: 1 CPU, 256 MiB) |
--disk FILE[:ro] |
A disk. Repeat for more |
--vsock PATH |
A vsock device. Programs here connect to the Unix socket PATH and send CONNECT <port>; the guest reaches port P here at PATH_P. A restored copy needs its own PATH |
--rootfs FILE -- COMMAND [ARG...] |
Boot into the EROFS image FILE and run COMMAND there; its output and exit status are the command's. --init must be shards-init |
--rootfs FILE --snapshot-dir DIR |
Boot into FILE and save a template to DIR once the image is mounted, for restore vm DIR -- COMMAND |
-e · -w · -u · --hostname · -i |
With --rootfs: the command's environment, directory, user, hostname and stdin |
--snapshot-dir DIR |
Save the microVM to DIR when it asks, then exit. --snapshot-then resume keeps it running. Works with restore too |
--no-console |
Hide the console |
Exit codes: 0 for shutdown or snapshot, 1 for an error, 2 for bad usage, 3 when the guest
reboots. With --rootfs, the command's own, or 125 to 127 when it could not run.
SHARDS_LOG=debug shows what shards is doing.
Images, the guest's kernel and shards-init, and saved microVMs are kept in SHARDS_HOME, if
you set it; otherwise in shards in your data directory (~/Library/Application Support on
macOS, ~/.local/share on Linux, %LOCALAPPDATA% on Windows). The programs shards writes
out for its microVMs are kept in SHARDS_CACHE, if you set it; otherwise in shards in your
cache directory (~/Library/Caches on macOS, $XDG_CACHE_HOME or ~/.cache on Linux,
%LOCALAPPDATA% on Windows).
Measured on an Apple M5 Max with macOS 26.4.1 at e8ec2e6: 100 runs each, 1 CPU, 256 MiB.
Details are in docs/benchmarks.md.
| Start | p50 | p99 |
|---|---|---|
Restore, preloaded with --hold |
149 µs | 197 µs |
| Restore, in a new process | 794 µs | 1.7 ms |
| Cold boot, to PID 1 | 21.2 ms | 22.2 ms |
Since then every microVM's process confines itself before it starts (on macOS, a sandbox that takes about 3.7 ms to set up): a restore in a new process and a cold boot take that much longer. A preloaded restore, and a run served by a microVM made ready ahead, do not.
Running a command in an image you have run before takes 3.0 ms at p50 and 3.4 ms at
p99, start to exit (shards run vm IMAGE exit 0), where a boot takes 34.8 ms. That is 300
runs over 10 saved copies, on a Mac running other VMs. Over a third of it is starting the
shards process. The command itself takes 0.4 ms inside the microVM: the microVM
waiting for your run has already touched the memory the first run of the image used.
With shards run vm -it, a key you type is echoed in 0.27 ms at p50 and 0.39 ms at p99,
where Docker Desktop on the same Mac takes 0.45 and 1.2 ms.
Linux itself takes 18.6 ms of a cold boot. That is why shards restores snapshots, and why a leaner kernel is coming.
Peak memory is 12.5 MiB for a restored microVM, 16.4 MiB once it has run a command, and 59.8 MiB for a booted one, guest memory included.
Against Firecracker v1.17.0 on one host (GitHub's x86_64 runner), with the same kernel and guest, 1 CPU and 128 MiB, 30 interleaved runs each:
| shards | Firecracker | |
|---|---|---|
| Boot to ready, p50 | 143 ms | 147 ms |
| Boot to ready, p99 | 162 ms | 228 ms |
| VMM memory | 2.4 MiB | 4.5 MiB |
The guest kernel dominates boot there, under nested virtualization. Snapshot restores are next.
- Two walls. The agents in one microVM are kept apart by its kernel: each in namespaces, IDs and a view of the files of its own, confined by Landlock and seccomp, reaching only what it is granted. The microVM keeps all of them away from your computer and from other microVMs: it has its own kernel and runs in its own process.
- One OS per microVM. Agents run on their microVM's OS instead of each bringing an image, so a snapshot holds one OS however many agents run on it.
- Speed. Shards boots once, snapshots, and restores copies. A preloaded copy only has to start running.
- Density. Copies share snapshot memory until they write to it.
- No root. Shards uses
/dev/kvmon Linux, Hypervisor.framework on macOS and the Windows Hypervisor Platform on Windows. - No crashes. Bad input gets an error message. Lints forbid code that can panic, and CI enforces them.
- Portability. One codebase covers Linux (glibc/musl) and Windows on amd64/arm64, and macOS on arm64. CI builds the seven supported targets; Intel Mac is unsupported.
The design and the evidence behind each decision are in docs/design/architecture.md.
| Doc | What's in it |
|---|---|
| Architecture | The design, each decision with its evidence, and the plan |
| Benchmarks | Every measurement, with its command, machine and revision |
| Release notes | What each release brings |
| Research | The papers, specifications and measurements behind the design |
| Brand | The logo |
cargo build -p shards
cargo test --workspace --release # boots real machines
cargo clippy --workspace --all-targets --target <triple> -- -D warnings
cargo bench -p shards --bench boot # and --bench restoreThe tests need what shards needs (see Install). They fetch the kernel and build
the guest programs themselves. Hosts that can't run machines print SKIP:. The project rules
are in CLAUDE.md.
Shards learned a great deal from Firecracker, and its guest kernel starts from Firecracker's microVM kernel configuration.
MIT — © 2026 Hyperlight. See LICENSE.