docs/sandboxes/lifecycle.mdx
Each sandbox runs as a child process of whatever application creates it. Sandbox.builder(...).create() boots a microVM, starts the guest agent inside it, and establishes a communication channel back to the host.
Understanding the lifecycle is useful once you start managing long-running sandboxes, graceful shutdown, or resilient agent workflows.
stateDiagram-v2
[*] --> Creating: create()
Creating --> Running: boot complete
Running --> Draining: drain()
Running --> Stopped: stop()
Running --> Crashed: unexpected exit
Draining --> Stopped: drain complete
Stopped --> Running: start()
Stopped --> [*]: remove()
Crashed --> [*]: remove()
| Status | Description |
|---|---|
| Creating | The VM is booting. The kernel is loaded, the filesystem is mounted, and the guest agent is initializing (configuring network, setting up the environment). |
| Running | The guest agent is ready. You can call exec, shell, and fs. |
| Draining | Graceful shutdown in progress. Existing commands run to completion, but new exec calls are rejected. Transitions to Stopped when all commands finish. |
| Stopped | The VM has shut down. Sandbox configuration and state are persisted to the database and can be restarted. |
| Crashed | The VM exited unexpectedly (e.g., kernel panic, OOM kill). |
Creating a sandbox boots the microVM, mounts the filesystem, initializes the guest agent, and waits until it's ready to accept commands. Names must be non-empty and no longer than 128 UTF-8 bytes.
<CodeGroup> ```rust Rust // Attached: sandbox stops when your process exits let sb = Sandbox::builder("worker").image("python").create().await?;// Detached: sandbox survives after your process exits let sb = Sandbox::builder("worker").image("python").create_detached().await?;
```typescript TypeScript
// Attached: sandbox stops when your process exits
await using sb = await Sandbox.builder("worker").image("python").create();
// Detached: sandbox survives after your process exits
const detached = await Sandbox.builder("worker").image("python").createDetached();
# Attached: sandbox stops when your process exits
sb = await Sandbox.create("worker", image="python")
# Detached: sandbox survives after your process exits
sb = await Sandbox.create("worker", image="python", detached=True)
await sb.detach()
// Attached: sandbox stops when your process exits
sb, err := m.CreateSandbox(ctx, "worker", m.WithImage("python"))
// Detached: sandbox survives after your process exits
detached, err := m.CreateSandbox(ctx, "worker",
m.WithImage("python"),
m.WithDetached(),
)
# Attached
msb create python --name worker
# Detached
msb run -d python --name worker
Stopping gracefully terminates guest processes and shuts down the VM. The sandbox moves to Stopped and can be restarted later with all its configuration preserved.
let sb = Sandbox::start("worker").await?;
```typescript TypeScript
await sb.stop()
// Later, resume where you left off
const sb = await Sandbox.start("worker")
await sb.stop()
# Later, resume where you left off
sb = await Sandbox.start("worker")
_ = sb.Stop(ctx)
// Later, resume where you left off
sb, err := m.StartSandbox(ctx, "worker")
msb stop worker
# Later, resume where you left off
msb start worker
If a sandbox is unresponsive (e.g., stuck in a tight loop or a panic), force-kill it. The sandbox is terminated immediately with no graceful shutdown.
<CodeGroup> ```rust Rust sb.kill().await?; ```await sb.kill()
await sb.kill()
err := sb.Kill(ctx)
msb stop --force worker
Keeps a sandbox running after the parent process exits. It becomes a background process that you can reconnect to later with Sandbox::get("worker").
await sb.detach()
await sb.detach()
err := sb.Detach(ctx)
Trigger a graceful shutdown that lets existing commands finish but rejects new ones. The sandbox moves to Draining and transitions to Stopped when all in-flight commands complete. This is useful for zero-downtime rotation of worker sandboxes.
await sb.drain()
await sb.drain()
err := sb.Drain(ctx)
Block until the sandbox exits on its own, without triggering a stop.
<CodeGroup> ```rust Rust let exit_status = sb.wait().await?; ```const exitStatus = await sb.wait()
code, success = await sb.wait()
code, err := sb.Wait(ctx)
Delete a stopped sandbox and its associated state from disk.
<CodeGroup> ```rust Rust Sandbox::remove("worker").await?; ```await Sandbox.remove("worker")
await Sandbox.remove("worker")
err := m.RemoveSandbox(ctx, "worker")
msb rm worker
const sandboxes = await Sandbox.list();
for (const handle of sandboxes) {
console.log(`${handle.name}: ${handle.status}`);
}
const handle = await Sandbox.get("worker");
console.log(handle.status); // "running" | "stopped" | ...
for handle in await Sandbox.list():
print(f"{handle.name}: {handle.status}")
handle = await Sandbox.get("worker")
print(handle.status) # "running" | "stopped" | ...
handles, err := m.ListSandboxes(ctx)
for _, handle := range handles {
fmt.Printf("%s: %s\n", handle.Name(), handle.Status())
}
handle, err := m.GetSandbox(ctx, "worker")
fmt.Println(handle.Status()) // "running" | "stopped" | ...
msb ls
msb ps worker
At runtime, your application talks to a host-side sandbox process, and that process relays requests to the guest agent inside the VM.
graph TD
subgraph Host["Host"]
A["Your Application
<small>microsandbox SDK</small>"]
B["Sandbox Process
<small>VM + networking + lifecycle</small>"]
end
subgraph Guest["Guest VM"]
F["agentd
<small>exec, fs</small>"]
end
A -- "spawn" --> B
B -- "boot" --> F
A -. "commands & responses" .-> F
style Host fill:#f5f0ff,stroke:#a770ef,color:#333
style Guest fill:#fef4e8,stroke:#e8a838,color:#333
style A fill:#d4bfff,stroke:#8b5cf6,color:#1a1a1a
style B fill:#d4bfff,stroke:#8b5cf6,color:#1a1a1a
style F fill:#fdd49e,stroke:#d97706,color:#1a1a1a
The sandbox process also handles:
Use msb logs or the SDK logs() method to read captured output from running, stopped, or crashed sandboxes. For source semantics, boot errors, and diagnostic flows, see Logs.
For production workloads, configure how the sandbox process handles shutdown, idle detection, and maximum lifetime.
<CodeGroup> ```rust Rust let sb = Sandbox::builder("worker") .image("python") .max_duration(3600) .idle_timeout(300) .create() .await?; ```await using sb = await Sandbox.builder("worker")
.image("python")
.maxDuration(3600) // maximum sandbox lifetime in seconds
.idleTimeout(300) // auto-drain after 5 minutes of inactivity
.create();
sb = await Sandbox.create(
"worker",
image="python",
max_duration=3600,
idle_timeout=300,
)
sb, err := m.CreateSandbox(ctx, "worker",
m.WithImage("python"),
m.WithMaxDuration(time.Hour),
m.WithIdleTimeout(5*time.Minute),
)