> ## 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.

# Lifecycle

> Create, start, stop, and manage sandbox state

Create a sandbox, stop and restart it as needed, and remove it when you are finished. Stopping keeps its configuration and filesystem; removing deletes its saved state.

## Create a sandbox

<Note>
  By default, sandboxes created through a local SDK stop when your application exits. To keep one running, [detach it](#keep-a-sandbox-running). Cloud sandboxes keep running until stopped or removed, subject to their lifetime limits.
</Note>

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

<CodeGroup>
  ```typescript TypeScript theme={null}
  await using sb = await Sandbox.builder("worker").image("python").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>

## Pause and resume

Pause freezes a local sandbox in memory. Resume continues where it left off. New commands are rejected while paused, and network connections may time out. Pause and resume are not supported on cloud.

Time spent paused does not count toward the [60-second limit for waiting input](#when-input-gets-stuck).
If new connections were already blocked, they stay blocked until input starts flowing again.

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

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

  ```python Python theme={null}
  worker = await Sandbox.get("worker")
  await worker.pause()
  await worker.resume()
  ```

  ```go Go theme={null}
  worker, err := m.GetSandbox(ctx, "worker")
  if err != nil { return err }
  if err := worker.Pause(ctx); err != nil { return err }
  if err := worker.Resume(ctx); err != nil { return err }
  ```

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

Use `resume` for a paused sandbox and `start` for a stopped one. Repeating `pause` or `resume` is safe. Pausing does not create a snapshot; taking a [full snapshot](/sandboxes/snapshots) while paused leaves it paused. Pause/resume requires a matching runtime and guest kernel.

## Stop and start again

Stopping shuts down the sandbox and its processes while keeping its configuration and filesystem. Starting it again boots a new VM; it does not resume the old processes.

`stop()` waits for graceful shutdown with no default timeout. Use your SDK's stop-with-timeout method to limit the wait. A timeout returns an error without force-killing; the shutdown may still finish later. Resume a paused sandbox before stopping it.

See the SDK references for timeout options and cancellation rules: [TypeScript](/sdk/typescript/sandbox), [Rust](/sdk/rust/sandbox), [Python](/sdk/python/sandbox), or [Go](/sdk/go/sandbox). Cancelling a wait does not disable automatic cleanup or [lifetime limits](#automatic-lifecycle-policies).

<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` to stop and start in one operation. If the sandbox is already stopped or crashed, it starts directly.

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

If graceful shutdown times out, restart fails without starting a new VM. For a sandbox that will not stop, see [Stop an unresponsive sandbox](#stop-an-unresponsive-sandbox).

## Reuse or create a named sandbox

Use `connect_or_create` to reuse a named sandbox or create it if it does not exist.

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 apply only to new sandboxes. An existing sandbox keeps its saved configuration. To recreate it with different settings, use [replacement locally](/sandboxes/overview#naming-conflicts), or remove and recreate it on cloud.
</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)
  ```

  ```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)
  ```

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

To detach a sandbox you already created, call `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())
  ```

  ```ruby Ruby theme={null}
  Microsandbox::Sandbox.list.fetch("sandboxes").each do |handle|
    puts "#{handle.name}: #{handle.status}"
  end

  handle = Microsandbox::Sandbox.get("worker")
  puts handle.status
  ```

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

## Wait for a state

Use `wait_until_stopped` to wait for a sandbox to stop without requesting a shutdown yourself. To request shutdown now and wait separately, use `request_stop` first; see your [SDK reference](#reference) for the method names.

<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?;
  ```

  ```python Python theme={null}
  ping = await sb.ping()
  print(f"agent reachable in {ping.latency_ms:.1f} ms")

  await sb.touch()
  ```

  ```go Go theme={null}
  ping, err := sb.Ping(ctx)
  if err != nil {
      return err
  }
  fmt.Printf("agent reachable in %s\n", ping.Latency)

  if _, err := sb.Touch(ctx); err != nil {
      return err
  }
  ```

  ```ruby Ruby theme={null}
  ping = sb.ping
  puts "agent reachable in #{ping.fetch("latency_ms")} ms"

  sb.touch
  ```

  ```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>

## Destroy or remove

Use `destroy` to stop and remove a sandbox in one operation. If graceful shutdown times out, it leaves the saved data intact. It will not remove a different sandbox that has reused the same 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),
  )
  ```

  ```ruby Ruby theme={null}
  sb = Microsandbox::Sandbox.create(
    "worker",
    image: "python",
    max_duration: 3600,
    idle_timeout: 300
  )
  ```
</CodeGroup>

## Lifecycle states

Use these states to display status or decide what to do next.

| 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. |
| **Paused** | The local VM and its RAM remain resident. Resume continues the same processes. |
| **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. |

## Names, handles, and concurrent callers

A name identifies the sandbox currently saved under it. An SDK handle identifies one specific sandbox. If that sandbox is removed and its name is reused, the old handle will not act on the replacement.

Concurrent callers can safely use `connect_or_create` or `connect_or_start` to reuse a sandbox. If it is still starting, they wait rather than start another VM. See your [SDK reference](#reference) for identity checks and replacement errors.

## Troubleshooting

### Stop an unresponsive sandbox

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

If [graceful stop](#stop-and-start-again) does not work, use `kill` to end the VM immediately. Running work is interrupted and unsaved data may be lost.

<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)
  ```

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

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

### When input gets stuck

If input has to wait because the sandbox’s input queue has been full for 60 seconds,
Microsandbox stops accepting new connections. It keeps existing connections and pending requests open, and
output and logs remain available. It does not stop the sandbox or its workloads.
New connections are accepted again once input starts flowing.

You can set request timeouts in your application. A timeout does not necessarily
mean the request failed to run.

### Logs and diagnostics

Use [`msb logs`](/cli/sandbox-commands#msb-logs) or the SDK `logs()` method to read sandbox output, even after it stops or crashes. See [Logs](/sandboxes/logs) for troubleshooting startup and other failures.

## 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).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.