Before a sandbox starts doing real work, you can prepare it three ways. Scripts bundle reusable commands. Patches modify the rootfs before the VM boots. A custom init system (systemd, OpenRC, s6) can run as PID 1 instead of microsandbox’s minimal agent. All three are defined at creation time and keep the base image untouched.Documentation Index
Fetch the complete documentation index at: https://docs.microsandbox.dev/llms.txt
Use this file to discover all available pages before exploring further.
Scripts
Scripts are files mounted at/.msb/scripts/ inside the sandbox. The directory is on PATH, so each script is callable by name through exec() or shell().
It provides a clean way to bundle setup procedures or entry points with a sandbox without baking them into the image.
Patches
Patches modify the rootfs before the VM boots. Write config files, copy directories from the host, create symlinks, append to existing files, remove things you don’t need. The base image stays untouched since patches are written to the writable layer on top. Patches are applied in order and work with OCI images and bind-mounted rootfs. They’re not supported with disk image roots (QCOW2, Raw).Available operations
The patch builder appends operations in the order you call them; calls are chainable. Available operations across SDKs:text, file, mkdir, append, copyFile / copy_file, copyDir / copy_dir, symlink, remove.
Full per-language signatures, parameters, and option shapes are documented in the SDK references:
- Rust:
PatchBuilder - TypeScript:
PatchBuilder - Python:
Patch(factory) andPatchConfig(the value type) - Go:
Patch(helpers) andPatchConfig(the value type)
Custom init system
By default the microsandbox agent runs as PID 1 inside the guest: small, fast, minimal. For workloads that expect a real init (systemd, OpenRC, s6, runit, etc.),--init hands PID 1 over to the init binary of your choice.
The handoff sequence:
- Agent does the boot-time setup: mount filesystems, configure network, prepare runtime dirs.
- Agent forks.
- Parent execs your init and becomes PID 1.
- Child agent continues serving host requests over the same channel.
systemctl to work.
The simplest entry point is auto, which probes a small list of well-known paths inside the guest rootfs and picks the first one that exists:
/sbin/init/lib/systemd/systemd/usr/lib/systemd/systemd
/proc/1/comm inside the sandbox. It should print the init’s name (systemd, init, etc.):
--init=auto can’t find anything in its candidate list, agentd fails boot with a clear error in kernel.log listing every path it checked. Switch to an explicit path (--init=/lib/systemd/systemd) when you know exactly where the init lives, or follow the image-picking guidance below to choose an image that actually ships one.
Argv and env
Pass extra argv and env to the init:- Rust / TypeScript:
init_with(...)/initWith(...). - Python: pass an
InitConfigtoinit=. - CLI: repeat
--init-argonce per entry, and--init-env KEY=VALonce per env var.
[<cmd>] when none is given; env is merged on top of the inherited environment.
Picking an image
Most slim Docker base images (debian:bookworm-slim, ubuntu:24.04, python:3.12-slim) are stripped of init binaries; they’re built for “one process per container” and don’t ship systemd at all. If you point --init at a path the image doesn’t contain, the agent’s pre-flight check fails boot with a clear error in the kernel log (no kernel panic).
Three ways to get an image with an init:
Use one of our published guest images
Use one of our published guest images
We maintain a small collection at
Multi-arch (
superradcompany/guest-images for the cases where you specifically need a real init. Current set:| Image | Pull |
|---|---|
| Debian + systemd | ghcr.io/superradcompany/debian-systemd:12 |
| Ubuntu + systemd | ghcr.io/superradcompany/ubuntu-systemd:24.04 |
| Fedora + systemd | ghcr.io/superradcompany/fedora-systemd:40 |
| Alpine + OpenRC | ghcr.io/superradcompany/alpine-openrc:3.20 |
linux/amd64 + linux/arm64), rebuilt weekly so they pick up upstream security patches, smoke-tested on every rebuild. Each image’s GHCR page documents its specific tag aliases and contents.Build a small custom image
Build a small custom image
Use a community-built systemd image
Use a community-built systemd image
Examples:
jrei/systemd-debian:12, jrei/systemd-ubuntu:22.04. These work out of the box but are published by community maintainers, not the distros themselves. Vet them like any other third-party image before running real workloads.alpine:3.20 ships BusyBox at /sbin/init, which is enough to exercise the handoff mechanics:
Shutdown semantics
Without--init, microsandbox shuts the guest down by remounting root read-only and calling reboot(RB_POWER_OFF). Typically <100 ms.
With --init, the agent isn’t PID 1 anymore, so it asks your init to shut down:
SIGRTMIN+4first (systemd’s poweroff signal).SIGTERMfallback for inits that don’t speak it.
| Init | Typical shutdown |
|---|---|
| BusyBox / s6 / runit | <100 ms |
| OpenRC | 50-500 ms |
| systemd | 1-5 s |
Init and entrypoint
--init and --entrypoint are orthogonal:
--initcontrols PID 1: what runs the system.--entrypoint(and the trailing-- cmd) controls what your workload runs.
exec calls land your shell, scripts, or app inside the systemd-managed environment.