Skip to main content

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.

See Overview for configuration examples and Lifecycle for state management.

Static methods


Sandbox.create()

@staticmethod
async def create(name_or_config: str | dict, **kwargs) -> Sandbox
Create and boot a sandbox. The first argument is either a name (str) or a full config (dict). Keyword arguments provide individual config fields. Pulls the image if needed, boots the VM, starts the guest agent, and waits until it is ready to accept commands. Parameters
NameTypeDescription
name_or_configstr | dictSandbox name or full configuration dict
imagestr | ImageSourceOCI image, local path, or disk image (required)
cpusintVirtual CPUs (default 1)
memoryintGuest memory in MiB (default 512)
workdirstrDefault working directory for commands
shellstrShell for shell() calls (default "/bin/sh")
hostnamestrGuest hostname
userstrDefault guest user
entrypointlist[str]Override the image’s stored ENTRYPOINT. Consulted by msb exec / msb run (CLI command resolution), not by sb.exec / sb.shell — those pass cmd literally
initstr | dict | InitConfigHand off PID 1 to a guest init binary. See Custom init system and InitConfig for accepted shapes
replaceboolReplace existing sandbox with same name (10s SIGTERM grace, then SIGKILL)
replace_with_gracefloatSeconds to wait after SIGTERM before escalating to SIGKILL (default 10; 0 skips SIGTERM). Implies replace=True
max_durationfloatMaximum sandbox lifetime in seconds
idle_timeoutfloatIdle timeout in seconds
envdict[str, str]Environment variables visible to all commands
scriptsdict[str, str]Named scripts mounted at /.msb/scripts/
pull_policystr | PullPolicyImage pull behavior
log_levelstr | LogLevelOverride log verbosity
registry_authRegistryAuthPrivate registry credentials
volumesdict[str, dict]Volume mounts. See Volumes.
patcheslist[PatchConfig]Rootfs modifications applied before boot
portsdict[int, int]Port mappings (host_port: guest_port)
networkNetworkNetwork policy and configuration
secretslist[SecretEntry]Secret injection
detachedboolIf True, sandbox survives after your process exits
Returns
TypeDescription
SandboxRunning sandbox
The returned Sandbox is an async context manager. Use async with to guarantee cleanup; on exit the sandbox is killed and its persisted state removed:
async with await Sandbox.create("my-sandbox", image="alpine") as sb:
    output = await sb.shell("echo hello")
    print(output.stdout_text)
# sandbox is automatically killed and removed on exit

Sandbox.create_with_progress()

@staticmethod
def create_with_progress(name_or_config: str | dict, **kwargs) -> PullSession
Same parameters as Sandbox.create() but returns a PullSession that lets you track image pull progress before the sandbox is ready. This method is synchronous (not awaitable) — the async work happens through the PullSession. Parameters Same as Sandbox.create(). Returns
TypeDescription
PullSessionSession for tracking pull progress and obtaining the final sandbox

Sandbox.start()

@staticmethod
async def start(name: str, *, detached: bool = False) -> Sandbox
Restart a previously stopped sandbox. The VM reboots using the persisted configuration. Parameters
NameTypeDescription
namestrName of a stopped sandbox
detachedboolIf True, sandbox survives after your process exits (default False)
Returns
TypeDescription
SandboxRunning sandbox

Sandbox.get()

@staticmethod
async def get(name: str) -> SandboxHandle
Get a handle to an existing sandbox (running or stopped). The handle provides status, configuration, and lifecycle control. Parameters
NameTypeDescription
namestrSandbox name
Returns
TypeDescription
SandboxHandleHandle with status and lifecycle control

Sandbox.list()

@staticmethod
async def list() -> list[SandboxHandle]
List all sandboxes (running, stopped, and crashed). Returns
TypeDescription
list[SandboxHandle]All sandboxes

Sandbox.remove()

@staticmethod
async def remove(name: str) -> None
Delete a stopped sandbox and all its state from disk. Fails if the sandbox is still running. Parameters
NameTypeDescription
namestrSandbox name

Instance properties


name

@property
async def name(self) -> str
Sandbox name. This is an async property — use await sb.name.

owns_lifecycle

@property
async def owns_lifecycle(self) -> bool
Whether this handle owns the sandbox lifecycle. True in attached mode, False in detached mode. This is an async property — use await sb.owns_lifecycle.

fs

@property
def fs(self) -> SandboxFs
Get a filesystem handle for reading and writing files inside the running sandbox. This is a synchronous property — use sb.fs (no await). See Filesystem for the full API. Returns
TypeDescription
SandboxFsFilesystem handle

Instance methods


attach()

async def attach(cmd: str, args_or_options: list[str] | ExecOptions | None = None) -> int
Bridge your terminal directly to a process inside the sandbox for a fully interactive PTY session. Parameters
NameTypeDescription
cmdstrCommand to run
args_or_optionslist[str] | ExecOptions | NoneCommand arguments or execution options
Returns
TypeDescription
intExit code of the process

attach_shell()

async def attach_shell() -> int
Attach to the sandbox’s default shell. Returns
TypeDescription
intExit code

detach()

async def detach() -> None
Release the handle without stopping the sandbox. Reconnect later with Sandbox.get().

drain()

async def drain() -> None
Start a graceful drain (SIGUSR1). Existing commands run to completion, new exec calls are rejected.

exec()

async def exec(cmd: str, args_or_options: list[str] | ExecOptions | None = None) -> ExecOutput
Run a command inside the sandbox and wait for it to complete. Collects all stdout and stderr into memory and returns them along with the exit code. For long-running processes or large output, use exec_stream() instead. Parameters
NameTypeDescription
cmdstrCommand to execute (e.g. "python", "/usr/bin/node")
args_or_optionslist[str] | ExecOptions | NoneCommand arguments (e.g. ["-c", "print('hello')"]) or ExecOptions
Returns
TypeDescription
ExecOutputCollected stdout, stderr, and exit status

exec_stream()

async def exec_stream(cmd: str, args_or_options: list[str] | ExecOptions | None = None) -> ExecHandle
Run a command with streaming output. Returns a handle that emits stdout, stderr, and exit events as they happen, rather than buffering everything. Parameters
NameTypeDescription
cmdstrCommand to execute
args_or_optionslist[str] | ExecOptions | NoneCommand arguments or ExecOptions
Returns
TypeDescription
ExecHandleStreaming handle for receiving events and controlling the process

kill()

async def kill() -> None
Force-terminate the sandbox immediately (SIGKILL). No graceful shutdown.

metrics()

async def metrics() -> SandboxMetrics
Get a point-in-time snapshot of the sandbox’s resource usage. Returns
TypeDescription
SandboxMetricsCPU, memory, disk, network metrics

metrics_stream()

async def metrics_stream(interval: float = 1.0) -> MetricsStream
Stream resource metrics at a regular interval. The returned stream supports both recv() and async for. Parameters
NameTypeDescription
intervalfloatSeconds between metric snapshots (default 1.0)
Returns
TypeDescription
MetricsStreamAsync stream of metrics

logs()

async def logs(
    tail: int | None = None,
    since_ms: float | None = None,
    until_ms: float | None = None,
    sources: list[str] | None = None,
) -> list[LogEntry]
Read captured output from the sandbox’s exec.log. Backed by an on-disk JSON Lines file the runtime writes via the relay tap. Works on running and stopped sandboxes alike — there is no protocol traffic. The same method is available on SandboxHandle for callers that don’t want to start the sandbox first. The default sources are "stdout", "stderr", and "output" (PTY-merged). Pass "system" to also include synthetic lifecycle markers and runtime/kernel diagnostic lines.
import asyncio
import microsandbox

async def main():
    handle = await microsandbox.Sandbox.get("web")

    # Default — all user-program output, regardless of pipe/pty mode
    entries = await handle.logs()

    for e in entries:
        source = {
            "stdout": "OUT",
            "stderr": "ERR",
            "output": "PTY",
            "system": "SYS",
        }[e.source]
        print(
            f"[{e.timestamp_ms / 1000:.3f}] "
            f"{source} {e.session_id}: {e.text().rstrip()}"
        )

    # Filtered: last 50 entries from the past hour, including system lines
    import time
    recent = await handle.logs(
        tail=50,
        since_ms=(time.time() - 3600) * 1000,
        sources=["stdout", "stderr", "output", "system"],
    )

asyncio.run(main())
Timestamps are exposed as float ms since the Unix epoch (UTC) for parity with SandboxMetrics.timestamp_ms. Convert to datetime if needed:
from datetime import datetime, timezone
dt = datetime.fromtimestamp(e.timestamp_ms / 1000, timezone.utc)
Parameters
NameTypeDescription
tailint | NoneShow only the last N entries after other filters apply
since_msfloat | NoneInclusive lower bound on entry timestamp (ms since epoch)
until_msfloat | NoneExclusive upper bound on entry timestamp (ms since epoch)
sourceslist[str] | NoneSources to include. None = ["stdout", "stderr", "output"]. Add "system" to merge runtime/kernel diagnostics. Use "all" as shorthand for all four.
Returns
TypeDescription
list[LogEntry]Matching entries in chronological order

remove_persisted()

async def remove_persisted() -> None
Remove the sandbox and all its persisted state from disk.

shell()

async def shell(script: str) -> ExecOutput
Run a command through the sandbox’s configured shell (defaults to /bin/sh). Shell syntax like pipes, redirects, and && chains works. Parameters
NameTypeDescription
scriptstrShell command string (e.g. "ls -la /app && echo done")
Returns
TypeDescription
ExecOutputCollected stdout, stderr, and exit status

shell_stream()

async def shell_stream(script: str) -> ExecHandle
Shell command with streaming output. Parameters
NameTypeDescription
scriptstrShell command string
Returns
TypeDescription
ExecHandleStreaming handle

stop()

async def stop() -> None
Gracefully shut down the sandbox (SIGTERM).

stop_and_wait()

async def stop_and_wait() -> tuple[int, bool]
Stop the sandbox and wait for the exit status. Returns
TypeDescription
tuple[int, bool](exit_code, success)success is True if exit code is 0

wait()

async def wait() -> tuple[int, bool]
Block until the sandbox exits on its own. Returns
TypeDescription
tuple[int, bool](exit_code, success)success is True if exit code is 0

Patch

Factory class for rootfs patches passed to Sandbox.create(..., patches=[...]). Each static method returns a PatchConfig. By default a patch that targets a path already present in the image errors at boot; pass replace=True on the operation to allow overwriting. mkdir and remove are idempotent. See Patches for conceptual context.

Patch.append()

@staticmethod
def append(path: str, content: str) -> PatchConfig
Append content to an existing file at path. If the file lives in a lower image layer, it’s copied up first. Parameters
NameTypeDescription
pathstrAbsolute path inside the guest
contentstrText to append

Patch.copy_dir()

@staticmethod
def copy_dir(src: str, dst: str, *, replace: bool = False) -> PatchConfig
Recursively copy a host directory at src into the guest rootfs at dst. Parameters
NameTypeDescription
srcstrHost source directory
dststrAbsolute destination path inside the guest
replaceboolWhen True, overwrite an existing path at dst

Patch.copy_file()

@staticmethod
def copy_file(
    src: str,
    dst: str,
    *,
    mode: int | None = None,
    replace: bool = False,
) -> PatchConfig
Copy a single host file at src into the guest rootfs at dst. Parameters
NameTypeDescription
srcstrHost source file
dststrAbsolute destination path inside the guest
modeint | NoneFile mode, e.g. 0o644. None keeps the source mode
replaceboolWhen True, overwrite an existing path at dst

Patch.mkdir()

@staticmethod
def mkdir(path: str, *, mode: int | None = None) -> PatchConfig
Create a directory at path. Idempotent: a no-op if the directory already exists. Parameters
NameTypeDescription
pathstrAbsolute path inside the guest
modeint | NoneDirectory mode, e.g. 0o755

Patch.remove()

@staticmethod
def remove(path: str) -> PatchConfig
Delete a file or directory at path. Idempotent: a no-op if the path doesn’t exist. Parameters
NameTypeDescription
pathstrAbsolute path inside the guest

@staticmethod
def symlink(target: str, link: str, *, replace: bool = False) -> PatchConfig
Create a symlink at link pointing to target. Parameters
NameTypeDescription
targetstrWhat the symlink points to (literal symlink target text)
linkstrAbsolute path of the symlink itself
replaceboolWhen True, overwrite an existing path at link

Patch.text()

@staticmethod
def text(
    path: str,
    content: str,
    *,
    mode: int | None = None,
    replace: bool = False,
) -> PatchConfig
Write UTF-8 text content at path. Parameters
NameTypeDescription
pathstrAbsolute path inside the guest
contentstrText content
modeint | NoneFile mode, e.g. 0o644
replaceboolWhen True, overwrite an existing path

Types

LogLevel

Sandbox process log verbosity.
ValueDescription
"debug"Debug and higher
"error"Errors only
"info"Info and higher
"trace"Most verbose — all diagnostic output
"warn"Warnings and errors only

LogEntry

A single captured log entry returned by logs().
Property / MethodTypeDescription
timestamp_msfloatWall-clock capture time (ms since Unix epoch, UTC)
sourcestrWhere the chunk came from. See LogSource.
session_idint | NoneRelay-monotonic session id; None for "system" entries
databytesThe chunk’s bytes (UTF-8 lossy decoded by default)
text()strConvenience: UTF-8 decode of data (lossy — invalid bytes are replaced)

LogSource

The string values that the source field on a LogEntry can take, also accepted by logs(sources=[...]).
ValueDescription
"stdout"Captured from a session’s stdout (pipe mode — streams stayed separated)
"stderr"Captured from a session’s stderr (pipe mode)
"output"Captured from a session running in PTY mode. PTY allocation merges stdout and stderr at the kernel level inside the guest, so they arrive as a single stream — tagged "output" rather than mislabelled as "stdout".
"system"Synthetic entry: lifecycle markers in exec.log plus runtime/kernel diagnostic lines merged in at read time when "system" is requested.
In addition, logs(sources=["all"]) is a shorthand for all four.

MetricsStream

Async stream for receiving periodic metrics snapshots.
MethodReturnsDescription
__aiter__AsyncIterator[SandboxMetrics]Use with async for
recv()SandboxMetrics | NoneReceive next snapshot. Returns None when the stream ends.

PatchConfig

A single rootfs patch. Produced by the Patch factory; you’d normally not construct one directly. Frozen dataclass.
FieldTypeDescription
kindstrOne of "text", "file", "copy_file", "copy_dir", "symlink", "mkdir", "remove", "append"
pathstr | NoneAbsolute guest path (used by text / file / mkdir / remove / append)
contentstr | NoneText content (text / append)
srcstr | NoneHost source path (copy_file / copy_dir)
dststr | NoneGuest destination path (copy_file / copy_dir)
targetstr | NoneSymlink target
linkstr | NoneSymlink path
modeint | NoneFile / directory mode (e.g. 0o644)
replaceboolWhen True, overwrite an existing path at the destination. Defaults to False

PullPolicy

Controls when the SDK fetches an OCI image from the registry.
ValueDescription
"always"Pull the image every time, even if cached locally
"if-missing"Pull only if the image is not already cached. This is the default.
"never"Never pull; fail if the image is not cached locally

PullProgress

Type alias for pull progress events emitted by PullSession.progress. Each event is one of the following frozen dataclasses, imported from microsandbox:
VariantFields
Resolvingreference: str
Resolvedreference: str, manifest_digest: str, layer_count: int, total_download_bytes: int | None
LayerDownloadProgresslayer_index: int, digest: str, downloaded_bytes: int, total_bytes: int | None
LayerDownloadCompletelayer_index: int, digest: str, downloaded_bytes: int
LayerExtractStartedlayer_index: int, diff_id: str
LayerExtractProgresslayer_index: int, bytes_read: int, total_bytes: int
LayerExtractCompletelayer_index: int, diff_id: str
LayerIndexStartedlayer_index: int
LayerIndexCompletelayer_index: int
PullCompletereference: str, layer_count: int
Narrow with isinstance:
from microsandbox import Sandbox, Resolved, LayerDownloadProgress

session = Sandbox.create_with_progress("my-sandbox", image="ubuntu:latest")
async with session:
    async for event in session.progress:
        if isinstance(event, Resolved):
            print(f"{event.layer_count} layers, {event.total_download_bytes} bytes")
        elif isinstance(event, LayerDownloadProgress):
            print(f"layer {event.layer_index}: {event.downloaded_bytes}/{event.total_bytes}")
    sb = await session.result()

PullSession

Returned by Sandbox.create_with_progress(). Use as an async context manager to track image pull progress.
session = Sandbox.create_with_progress("my-sandbox", image="ubuntu:latest")
async with session:
    async for event in session.progress:
        print(event)
    sb = await session.result()
Property / MethodTypeDescription
progressAsyncIterator[PullProgress]Async iterator of pull progress events
result()Awaitable[Sandbox]Await to get the final running sandbox

RegistryAuth

Private registry credentials.
FieldTypeDescription
usernamestrRegistry username
passwordstrRegistry password

SandboxConfig

The keyword arguments accepted by Sandbox.create() and Sandbox.create_with_progress().
FieldTypeDefaultDescription
imagestr | ImageSource-OCI image, local path, or disk image (required)
cpusint1Virtual CPUs
memoryint512Guest memory in MiB. This is a limit, not a reservation.
workdirstr-Default working directory for commands
shellstr"/bin/sh"Shell for shell() calls
hostnamestr-Guest hostname
userstr-Default guest user
entrypointlist[str]-Override the image’s stored ENTRYPOINT. Consulted by msb exec / msb run (CLI command resolution), not by sb.exec / sb.shell — those pass cmd literally
initstr | dict | InitConfig-Hand off PID 1 to a guest init binary. See Custom init system
replaceboolFalseReplace existing sandbox with same name
max_durationfloat-Maximum sandbox lifetime in seconds
idle_timeoutfloat-Idle timeout in seconds
envdict[str, str]{}Environment variables visible to all commands
scriptsdict[str, str]{}Named scripts mounted at /.msb/scripts/
pull_policystr | PullPolicy"if-missing"Image pull behavior
log_levelstr | LogLevel-Override log verbosity
registry_authRegistryAuth-Private registry credentials
volumesdict[str, dict]{}Volume mounts. See Volumes.
patcheslist[PatchConfig][]Rootfs modifications applied before boot
portsdict[int, int]{}Port mappings (host_port: guest_port)
networkNetworkpublic_onlyNetwork policy and configuration
secretslist[SecretEntry][]Secret injection
detachedboolFalseIf True, sandbox survives after your process exits

InitConfig

Custom init specification. Pass it (or one of the equivalent shorthand shapes) as the init= kwarg to Sandbox.create() to hand PID 1 inside the guest off to your own init binary after agentd’s setup. See Custom init system for image picks, shutdown semantics, and tradeoffs.
from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class InitConfig:
    cmd: str
    args: tuple[str, ...] = ()
    env: Mapping[str, str] = {}
FieldTypeDescription
cmdstrAbsolute path or "auto" to the init binary inside the guest rootfs
argstuple[str, ...]Supplemental argv (argv[0] is implicitly cmd)
envdict[str, str]Extra env vars merged on top of the inherited env
The init= kwarg follows the same shape as other Sandbox.create kwargs that carry structured values (image=, network=, etc.): a bare scalar for the simple case, or a dataclass / dict for the rich case.
FormEquivalent to
init="auto" or init="/sbin/init"InitConfig(cmd=...)
init={"cmd": ..., "args": [...], "env": {...}}dict equivalent of InitConfig
init=InitConfig(cmd="/sbin/init", args=("--foo",))itself
from microsandbox import InitConfig, Sandbox

# Common case: bare string.
sb = await Sandbox.create("worker", image="jrei/systemd-debian:12", init="auto")

# Argv / env: dataclass.
sb = await Sandbox.create(
    "worker",
    image="jrei/systemd-debian:12",
    init=InitConfig(
        cmd="/lib/systemd/systemd",
        args=("--unit=multi-user.target",),
        env={"container": "microsandbox"},
    ),
)

SandboxHandle

A lightweight handle to an existing sandbox (running or stopped). Obtained via Sandbox.get() or Sandbox.list(). Provides status, configuration, and lifecycle control without an active connection to the guest agent. Call .start() or .connect() to upgrade to a full Sandbox.
Property / MethodTypeDescription
namestrSandbox name
statusstrCurrent status ("running", "stopped", "crashed", "draining", "paused")
config_jsonstrRaw JSON configuration
created_atfloat | NoneCreation timestamp (ms since epoch)
updated_atfloat | NoneLast update timestamp (ms since epoch)
connect()Awaitable[Sandbox]Connect to a running sandbox
start(*, detached=False)Awaitable[Sandbox]Start in attached or detached mode
stop()Awaitable[None]Graceful shutdown
kill()Awaitable[None]Force terminate
remove()Awaitable[None]Delete sandbox and state
metrics()Awaitable[SandboxMetrics]Point-in-time resource metrics
logs()Awaitable[list[LogEntry]]Read captured exec.log (works without starting)

SandboxMetrics

Point-in-time resource usage snapshot.
FieldTypeDescription
cpu_percentfloatCPU usage as a percentage
disk_read_bytesintTotal bytes read from disk since boot
disk_write_bytesintTotal bytes written to disk since boot
memory_bytesintCurrent memory usage in bytes
memory_limit_bytesintMemory limit in bytes
net_rx_bytesintTotal bytes received over the network since boot
net_tx_bytesintTotal bytes sent over the network since boot
timestamp_msfloatWhen this measurement was taken (ms since epoch)
uptime_msintTime since the sandbox was created (ms)