> ## Documentation Index
> Fetch the complete documentation index at: https://microsanbox-staging-toks-cloud-snapshot-contracts.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle

> Create, start, stop, and manage sandbox state

A sandbox has a simple lifecycle: create it, use it, stop it when it is idle, start it again when you need it, and remove it when you are finished. Stopping preserves the sandbox's configuration and filesystem, while removing deletes its sandbox-owned state.

## Create a sandbox

<Note>
  Locally, an attached SDK handle stops its sandbox when the client process exits. Cloud sandboxes are service-owned, so stop or remove them explicitly.
</Note>

Creating a sandbox starts the microVM and waits until it is ready to accept commands. Give each sandbox a name so you can find and manage it later. Names must be non-empty and no longer than 128 UTF-8 bytes.

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Local attached handle: 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")
    .detached(true)
    .create();
  ```

  ```rust Rust theme={null}
  let sb = Sandbox::builder("worker")
      .image("python")
      .create()
      .await?;
  ```

  ```python Python theme={null}
  sb = await Sandbox.create("worker", image="python")
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "worker", m.WithImage("python"))
  ```

  ```ruby Ruby theme={null}
  sb = Microsandbox::Sandbox.create("worker", image: "python")
  ```

  ```bash CLI theme={null}
  msb create python --name worker
  ```
</CodeGroup>

An attached local SDK handle normally stops its sandbox when the client process exits. See [Keep a sandbox running](#keep-a-sandbox-running) when the sandbox should outlive that process.

## Stop and start again

Stopping gracefully terminates guest processes and shuts down the VM. The sandbox moves to `Stopped`, but its configuration and filesystem remain available for the next start.

The timeout behavior depends on the backend:

* **Local:** `stop()` waits up to 10 seconds, then force-kills the sandbox if it is still running.
* **Cloud:** `stop()` waits up to 6 minutes because shutdown may include creating a durable disk checkpoint. If the deadline expires, the SDK returns its typed stop-timeout error and stops waiting. It does not cancel or force-kill the accepted server-side stop, which may still complete afterward.

Use the explicit timeout option in your SDK to change how long the caller waits. On Cloud this remains an observation deadline, not a cancellation deadline. Use `request_stop` and `wait_until_stopped` separately when you want to submit the stop and manage observation yourself.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await sb.stop();

  const sb = await Sandbox.start("worker");
  ```

  ```rust Rust theme={null}
  sb.stop().await?;

  let sb = Sandbox::start("worker").await?;
  ```

  ```python Python theme={null}
  await sb.stop()

  sb = await Sandbox.start("worker")
  ```

  ```go Go theme={null}
  _ = sb.Stop(ctx)

  sb, err := m.StartSandbox(ctx, "worker")
  ```

  ```ruby Ruby theme={null}
  sb.stop

  sb = Microsandbox::Sandbox.start("worker")
  ```

  ```bash CLI theme={null}
  msb stop worker
  msb start worker
  ```
</CodeGroup>

Use `restart` when you want to stop and start the sandbox in one operation. If it is already stopped or crashed, restart starts it directly. SDK receiver methods preserve the sandbox's identity and configuration while replacing only the running VM instance.

```bash CLI theme={null}
msb restart worker
```

On microsandbox cloud, timeout expiry cannot escalate to a force kill. Force controls are local-only, and detached-start controls affect only local process ownership.

## Reuse or create a named sandbox

Use `connect_or_create` when your application wants a sandbox with a stable name but does not know whether it already exists. This is useful for workers, development environments, and services that reconnect after the client process restarts.

The operation:

* Connects if the sandbox is running
* Starts it if it is stopped or crashed
* Creates it if the name does not exist

<CodeGroup>
  ```typescript TypeScript theme={null}
  const sb = await Sandbox.builder("worker")
    .image("python")
    .memory(MiB(1024))
    .connectOrCreate();
  ```

  ```rust Rust theme={null}
  let sb = Sandbox::builder("worker")
      .image("python")
      .memory(1024)
      .connect_or_create()
      .await?;
  ```

  ```python Python theme={null}
  sb = await Sandbox.connect_or_create(
      "worker",
      image="python",
      memory=1024,
  )
  ```

  ```go Go theme={null}
  sb, err := m.ConnectOrCreateSandbox(ctx, "worker",
      m.WithImage("python"),
      m.WithMemory(1024),
  )
  ```

  ```ruby Ruby theme={null}
  sb = Microsandbox::Sandbox.connect_or_create(
    "worker",
    image: "python",
    memory: 1024
  )
  ```
</CodeGroup>

<Warning>
  Creation options are used only when a new sandbox is created. If `worker` already exists, its saved image, resources, environment, mounts, and other configuration are kept. To apply new configuration, use the [local replacement workflow](/sandboxes/overview#naming-conflicts). On microsandbox cloud, where replacement is unavailable, remove the existing sandbox and then create it again.
</Warning>

If you already have a `SandboxHandle`, use `connect_or_start` to connect to that exact sandbox or start it when needed.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const handle = await Sandbox.get("worker");
  const sb = await handle.connectOrStart();
  ```

  ```rust Rust theme={null}
  let handle = Sandbox::get("worker").await?;
  let sb = handle.connect_or_start().await?;
  ```

  ```python Python theme={null}
  handle = await Sandbox.get("worker")
  sb = await handle.connect_or_start()
  ```

  ```go Go theme={null}
  handle, err := m.GetSandbox(ctx, "worker")
  sb, err := handle.ConnectOrStart(ctx)
  ```

  ```ruby Ruby theme={null}
  handle = Microsandbox::Sandbox.get("worker")
  sb = handle.connect_or_start
  ```
</CodeGroup>

## Keep a sandbox running

Detach a local sandbox when it should keep running after the client process exits. You can reconnect to it later by name.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const sb = await Sandbox.builder("worker")
    .image("python")
    .detached(true)
    .create();
  ```

  ```rust Rust theme={null}
  let sb = Sandbox::builder("worker")
      .image("python")
      .detached(true)
      .create()
      .await?;
  ```

  ```python Python theme={null}
  sb = await Sandbox.create("worker", image="python", detached=True)
  await sb.detach()
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "worker",
      m.WithImage("python"),
      m.WithDetached(),
  )
  ```

  ```ruby Ruby theme={null}
  sb = Microsandbox::Sandbox.create("worker", image: "python", detached: true)
  sb.detach
  ```

  ```bash CLI theme={null}
  msb run -d python --name worker
  ```
</CodeGroup>

You can also detach an existing SDK receiver with `detach()` (`Detach` in Go).

## List and inspect

List sandboxes to discover what exists, or get one by name when you already know which sandbox you need.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const page = await Sandbox.list();
  for (const handle of page.sandboxes) {
    console.log(`${handle.name}: ${handle.status}`);
  }

  const handle = await Sandbox.get("worker");
  console.log(handle.status);
  ```

  ```rust Rust theme={null}
  for handle in Sandbox::list().await?.sandboxes {
      println!("{}: {:?}", handle.name(), handle.status_snapshot());
  }
  ```

  ```python Python theme={null}
  for handle in (await Sandbox.list()).sandboxes:
      print(f"{handle.name}: {handle.status}")

  handle = await Sandbox.get("worker")
  print(handle.status)
  ```

  ```go Go theme={null}
  page, err := m.ListSandboxes(ctx)
  for _, handle := range page.Sandboxes {
      fmt.Printf("%s: %s\n", handle.Name(), handle.Status())
  }

  handle, err := m.GetSandbox(ctx, "worker")
  fmt.Println(handle.Status())
  ```

  ```bash CLI theme={null}
  msb ls
  msb ps worker
  ```
</CodeGroup>

## Wait for a state

Use `wait_until_stopped` when another part of your application is responsible for stopping the sandbox and you only need to wait for it to finish.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await sb.waitUntilStopped();
  ```

  ```rust Rust theme={null}
  let result = sb.wait_until_stopped().await?;
  ```

  ```python Python theme={null}
  result = await sb.wait_until_stopped()
  ```

  ```go Go theme={null}
  result, err := sb.WaitUntilStopped(ctx)
  ```

  ```ruby Ruby theme={null}
  result = sb.wait_until_stopped
  ```
</CodeGroup>

Use `wait_for_status` (`waitForStatus` in TypeScript and `WaitForStatus` in Go) when you need to wait for a specific lifecycle state. It has no built-in timeout, so use the language's normal timeout or cancellation primitive around it.

## Change configuration

Use `msb modify`, or the SDK `modify()` methods, to change an existing sandbox without recreating it. Some changes apply immediately, some affect future commands, and some take effect after a restart.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const plan = await sandbox.modify({ cpus: 4, memory: 4096 });
  ```

  ```rust Rust theme={null}
  let plan = sb.modify()
      .cpus(4)
      .memory(4096)
      .apply()
      .await?;
  ```

  ```python Python theme={null}
  plan = await sb.modify(cpus=4, memory=4096)
  ```

  ```go Go theme={null}
  plan, err := sb.Modify(ctx, m.ModifyOptions{CPUs: 4, MemoryMiB: 4096})
  ```

  ```bash CLI theme={null}
  msb modify api --cpus 4 --memory 4G
  ```
</CodeGroup>

See [Live Modify](/sandboxes/tuning) for the change model, CPU and memory resize, labels, environment variables, secrets, and storage sizing.

## Check health and keep a sandbox active

<Note>
  Ping and touch are currently available only for local sandboxes.
</Note>

Use `ping` to check that a running sandbox's guest agent is reachable. A ping is only a health check and does not reset the idle timer. Use `touch` when you intentionally want to keep the sandbox active.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const ping = await sb.ping();
  console.log(`agent reachable in ${ping.latencyMs.toFixed(1)} ms`);

  await sb.touch();
  ```

  ```rust Rust theme={null}
  let ping = sb.ping().await?;
  println!("agent reachable in {:?}", ping.latency);

  sb.touch().await?;
  ```

  ```bash CLI theme={null}
  msb ping worker
  msb touch worker

  # Check health, then keep the sandbox active if it is reachable
  msb ping worker --touch
  ```
</CodeGroup>

## Drain before stopping

<Note>
  Draining is currently available only for local sandboxes. Use a graceful stop on microsandbox cloud.
</Note>

Use a drain when existing commands should finish but new commands should be rejected. The sandbox moves to `Draining`, waits for in-flight commands, and then stops. This is useful when rotating worker sandboxes without interrupting active jobs.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await sb.requestDrain();
  ```

  ```rust Rust theme={null}
  sb.request_drain().await?;
  ```

  ```python Python theme={null}
  await sb.request_drain()
  ```

  ```go Go theme={null}
  err := sb.RequestDrain(ctx)
  ```
</CodeGroup>

## Stop an unresponsive sandbox

<Note>
  Force kill is currently available only for local sandboxes. Use a graceful stop on microsandbox cloud.
</Note>

If a sandbox does not respond to a graceful stop, force-kill it. This ends the VM immediately without waiting for guest processes to shut down.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await sb.kill();
  ```

  ```rust Rust theme={null}
  sb.kill().await?;
  ```

  ```python Python theme={null}
  await sb.kill()
  ```

  ```go Go theme={null}
  err := sb.Kill(ctx)
  ```

  ```bash CLI theme={null}
  msb stop --force worker
  ```
</CodeGroup>

## Destroy or remove

Use `destroy` when you have an SDK receiver and want to stop and remove that exact sandbox in one operation. It requests a graceful stop by default, escalates after the configured timeout, and refuses to act on a replacement that reused the name.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await sb.destroy();
  ```

  ```rust Rust theme={null}
  sb.destroy().await?;
  ```

  ```python Python theme={null}
  await sb.destroy()
  ```

  ```go Go theme={null}
  err := sb.Destroy(ctx)
  ```

  ```ruby Ruby theme={null}
  sb.destroy
  ```
</CodeGroup>

Use `remove` when the sandbox is already stopped and you want to delete it by name.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await Sandbox.remove("worker");
  ```

  ```rust Rust theme={null}
  Sandbox::remove("worker").await?;
  ```

  ```python Python theme={null}
  await Sandbox.remove("worker")
  ```

  ```go Go theme={null}
  err := m.RemoveSandbox(ctx, "worker")
  ```

  ```ruby Ruby theme={null}
  Microsandbox::Sandbox.remove("worker")
  ```

  ```bash CLI theme={null}
  msb rm worker
  ```
</CodeGroup>

For a local sandbox, removal deletes sandbox-owned state while leaving independently managed resources intact:

| Removed                                                                        | Kept                                   |
| ------------------------------------------------------------------------------ | -------------------------------------- |
| Sandbox record, configuration, status, labels, and run history                 | Cached OCI images and layers           |
| Managed OCI writable root disk (`upper.ext4`) and its guest filesystem changes | Named volumes and their contents       |
| Captured sandbox logs                                                          | Snapshots created from the sandbox     |
| Runtime staging files, including generated scripts                             | Bind-mounted host files or directories |
| Root filesystem pin metadata for this sandbox                                  | User-supplied root disk images         |

Removing a sandbox does not undo writes made to a named volume, bind mount, or user-supplied disk image. On cloud, removal deletes the remote sandbox resource; the local disk details above do not apply.

## Automatic lifecycle policies

For production workloads, configure a maximum lifetime or idle timeout so sandboxes shut down automatically.

<CodeGroup>
  ```typescript TypeScript theme={null}
  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();
  ```

  ```rust Rust theme={null}
  let sb = Sandbox::builder("worker")
      .image("python")
      .max_duration(3600)
      .idle_timeout(300)
      .create()
      .await?;
  ```

  ```python Python theme={null}
  sb = await Sandbox.create(
      "worker",
      image="python",
      max_duration=3600,
      idle_timeout=300,
  )
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "worker",
      m.WithImage("python"),
      m.WithMaxDuration(time.Hour),
      m.WithIdleTimeout(5*time.Minute),
  )
  ```
</CodeGroup>

## Lifecycle states

Most applications only need to distinguish between `Running` and `Stopped`. The complete set is useful for status displays and recovery logic.

| Status       | Meaning                                                                                                  |
| ------------ | -------------------------------------------------------------------------------------------------------- |
| **Created**  | Configuration has been saved, but the sandbox has not started yet.                                       |
| **Starting** | The VM and guest agent are starting. Commands are not ready yet.                                         |
| **Running**  | The sandbox is ready for `exec`, `shell`, and filesystem operations.                                     |
| **Draining** | Existing commands may finish, but new commands are rejected. The sandbox stops when the drain completes. |
| **Stopped**  | The VM is off. Configuration and sandbox state are preserved for a later start.                          |
| **Crashed**  | The VM exited unexpectedly and can be started again.                                                     |

Some backends can also report `Paused`. Resume is not currently exposed through the SDKs, so start and connect operations do not treat a paused sandbox as stopped.

## Names, handles, and concurrent callers

A sandbox name finds the current sandbox saved under that name. A `Sandbox` or `SandboxHandle` also carries an opaque stable ID (`ID()` in Go) for one exact saved sandbox. Treat the ID as an opaque value and use it for logging, correlation, and equality checks.

If `worker` is removed and a different sandbox is later created with the same name, an old receiver will not act on the replacement. Lifecycle methods return `SandboxReplaced` or the language's typed equivalent when they can detect this situation; an ID-addressed cloud request may instead report that the old resource no longer exists.

Concurrent callers are safe to use with the named lifecycle APIs. `create` remains strict, so only one same-name creation succeeds. `connect_or_create` may reuse the sandbox created by another caller, and `connect_or_start` remains attached to the exact identity held by its handle. Calls that encounter `Starting` wait for the sandbox to become ready instead of launching a second runtime.

## Logs and diagnostics

Use [`msb logs`](/cli/sandbox-commands#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](/sandboxes/logs).

## Reference

For exact lifecycle APIs, see [TypeScript](/sdk/typescript/sandbox), [Rust](/sdk/rust/sandbox), [Python](/sdk/python/sandbox), or [Go](/sdk/go/sandbox). For lifecycle commands and the REST surface, see [Sandbox commands](/cli/sandbox-commands) and the [Cloud API](/api-reference/overview).
