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

# Metrics

> Monitor sandbox resource usage

<Tooltip tip="SDK and CLI resource metrics are not yet available on microsandbox cloud; instrument the workload with an external monitoring system."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

Query resource usage for a running sandbox: CPU, memory, and more. Get a single point-in-time snapshot, or open a streaming subscription that delivers updates at a fixed interval.

While a sandbox is paused, the runtime skips host page-residency scans and reports `memory_host_resident_bytes` as unavailable (`null`, `None`, or `nil`, depending on the SDK). Host residency can change even when guest execution is stopped, so the runtime does not reuse the previous value. Other metrics continue to be sampled; residency sampling returns on the next sample after resume.

OCI sandboxes also expose optional upper filesystem fields for capacity dashboards. Guest-visible `upper_used_bytes` and `upper_free_bytes` are available when the protected bundled-kernel reporter is fresh. Host-observed `upper_host_allocated_bytes` is available when microsandbox can inspect the writable upper image from the host. SDKs surface those as nullable fields (`Option`, `null`, `None`, or `nil`) because custom kernels and non-OCI roots may not provide them.

<Note>
  Need continuous shipping to Grafana, Datadog, Prometheus, or any other OTel-compatible backend? See [`msb-metrics`](/observability/msb-metrics), a sidecar binary that reads the same data and ships it over OTLP.
</Note>

## Point-in-time

<CodeGroup>
  ```typescript TypeScript theme={null}
  const metrics = await sb.metrics();
  console.log(`CPU: ${metrics.cpuPercent.toFixed(1)}%, Mem: ${Math.floor(metrics.memoryBytes / 1024 / 1024)} MB`);
  ```

  ```rust Rust theme={null}
  use microsandbox::Sandbox;

  let metrics = sb.metrics().await?;
  println!("CPU: {:.1}%, Mem: {} MB", metrics.cpu_percent, metrics.memory_bytes / 1024 / 1024);
  ```

  ```python Python theme={null}
  m = await sb.metrics()
  print(f"CPU: {m.cpu_percent:.1f}%, Mem: {m.memory_bytes // 1024 // 1024} MB")
  ```

  ```go Go theme={null}
  metrics, err := sb.Metrics(ctx)
  fmt.Printf("CPU: %.1f%%, Mem: %d MB\n", metrics.CPUPercent, metrics.MemoryBytes/1024/1024)
  ```

  ```ruby Ruby theme={null}
  metrics = sb.metrics.to_h
  puts format("CPU: %.1f%%, Mem: %d MB",
    metrics.fetch("cpu_percent"), metrics.fetch("memory_bytes") / 1024 / 1024)
  ```
</CodeGroup>

## Streaming

Subscribe to metric updates at a regular interval. Each update arrives as a separate event.

<CodeGroup>
  ```typescript TypeScript theme={null}
  for await (const m of await sb.metricsStream(1000)) {
      console.log(`[${m.timestamp.toISOString()}] CPU: ${m.cpuPercent.toFixed(1)}%`);
  }
  ```

  ```rust Rust theme={null}
  use std::time::Duration;
  use futures::StreamExt;

  let mut stream = sb.metrics_stream(Duration::from_secs(1));
  while let Some(m) = stream.next().await {
      let m = m?;
      println!("CPU: {:.1}%, Mem: {} MB", m.cpu_percent, m.memory_bytes / 1024 / 1024);
  }
  ```

  ```python Python theme={null}
  async for m in await sb.metrics_stream(interval=1.0):
      print(f"[{m.timestamp_ms}] CPU: {m.cpu_percent:.1f}%")
  ```

  ```go Go theme={null}
  stream, err := sb.MetricsStream(ctx, time.Second)
  defer stream.Close()
  for {
      metrics, err := stream.Recv(ctx)
      if err != nil {
          return err
      }
      if metrics == nil {
          break
      }
      fmt.Printf("CPU: %.1f%%\n", metrics.CPUPercent)
  }
  ```
</CodeGroup>

## Fleet-wide metrics

Get the latest metrics for every running sandbox at once. Useful for dashboards or capacity planning.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { allSandboxMetrics } from "microsandbox";

  const all = await allSandboxMetrics();
  ```

  ```rust Rust theme={null}
  use microsandbox::all_sandbox_metrics;

  let all = all_sandbox_metrics().await?;
  ```

  ```python Python theme={null}
  from microsandbox import all_sandbox_metrics

  all_metrics = await all_sandbox_metrics()
  ```

  ```go Go theme={null}
  all, err := m.AllSandboxMetrics(ctx)
  ```
</CodeGroup>

Fleet snapshots only include sandboxes whose runtime process is actually alive. A runtime that crashed without cleanup used to keep reporting its last sample as if the sandbox were running; readers now detect the dead owner and retire the entry on first read.

## Metrics reports (Rust)

The Rust SDK additionally exposes *reports*: metrics joined with catalog context for presentation. A report resolves the CPU and memory allocations from the sandbox's **active config**, so live resizes are reflected. It also carries a state field: `Running`, `Stalled` (runtime alive but no sample within three sampling intervals), or `Exited` (the preserved terminal sample of a stopped sandbox). This is what `msb metrics` renders.

```rust Rust theme={null}
use microsandbox::{all_sandbox_metrics_reports_local, sandbox_metrics_report_local};

// All sandboxes; pass true to include exited ones.
let reports = all_sandbox_metrics_reports_local(&local, false).await?;
for r in &reports {
    println!("{} [{:?}] {:.2} cores of {:?}", r.name, r.state, r.metrics.cpu_percent / 100.0, r.cpus);
}

// One sandbox by name, in any state (unlike `Sandbox::metrics`, which
// errors when the sandbox is not running).
let report = sandbox_metrics_report_local(&local, "my-app").await?;
```

Metrics reports are currently available only in the Rust SDK.

## Reference

For exact per-sandbox metrics APIs, see [TypeScript](/sdk/typescript/sandbox), [Rust](/sdk/rust/sandbox), [Python](/sdk/python/sandbox), or [Go](/sdk/go/sandbox). For local reports and fleet snapshots, see [`msb metrics`](/cli/sandbox-commands#msb-metrics).

## Upgrading with running sandboxes

Upgraded SDK readers search both the registry derived from the normalized home and the legacy registry derived from its original spelling. Existing runtimes keep writing to their current registry and do not need restarting; each newly started run writes only to the normalized-home registry. All metrics APIs validate matches against the catalog and process identity and merge results without duplicate runs.

This uses in-memory reader state only: there is no database migration, sidecar, config rewrite, or change to registry cleanup. Keep the original home spelling available when upgrading. Discovering a legacy registry after that spelling is lost is outside this lookup's scope. Older readers do not automatically gain support for the new registry names.


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