# How to Monitor Resource Usage of a microsandbox Instance: Complete Guide with SDK and CLI

> Learn how to monitor microsandbox instance resource usage with our guide. Query CPU, memory, disk, and network metrics using the Rust SDK or CLI.

- Repository: [Super Rad Company/microsandbox](https://github.com/superradcompany/microsandbox)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Microsandbox records CPU, memory, disk I/O, network I/O, and uptime metrics in a shared-memory registry that you can query via the Rust SDK or CLI commands.**

This guide covers how to monitor resource usage of microsandbox instances using both programmatic interfaces and command-line tools. The microsandbox project implements a lightweight, real-time metrics system that balances granularity with minimal overhead.

## Understanding the Metrics Architecture

Microsandbox stores runtime metrics in a **shared-memory registry** (`microsandbox_metrics` crate). The collector writes samples from the runtime, enabling low-latency reads without interrupting the sandbox itself.

The registry lives in [`crates/metrics/src/lib.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/metrics/src/lib.rs) and is populated by the collector in [`crates/metrics-collector/src/lib.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/metrics-collector/src/lib.rs). This design lets you query metrics from any language SDK or the CLI without requiring in-process instrumentation.

## Configuring Metrics Sampling

Before monitoring, ensure metrics collection is enabled. You control this through the sandbox configuration.

In [`sdk/rust/lib/sandbox/config.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/config.rs) (lines 192-199), the `SandboxConfig::effective_metrics_interval` resolves the actual sampling rate:

```rust
// From config.rs - effective interval resolution
pub fn effective_metrics_interval(&self) -> Option<Duration> {
    self.metrics_sample_interval_ms
        .map(|ms| Duration::from_millis(ms as u64))
        .or_else(|| Some(DEFAULT_METRICS_INTERVAL))
}

```

Set the interval during sandbox creation:

```rust
use microsandbox::SandboxBuilder;
use std::time::Duration;

let sandbox = SandboxBuilder::new()
    .name("production-api")
    .image("docker.io/library/alpine:latest")
    .metrics_sample_interval(Duration::from_millis(750))  // 750ms sampling
    .run()
    .await?;

```

- **Shorter intervals** (100-500ms): Higher granularity for debugging or bursty workloads
- **Longer intervals** (5-10s): Lower overhead for steady-state monitoring
- **Disabled**: Set `metrics_sample_interval_ms: None` to turn off collection entirely

The effective configuration is resolved live, so metrics reflect any runtime resizes without restarting the sandbox.

## Fetching a Single Metrics Snapshot

For point-in-time monitoring, use `Sandbox::metrics()` defined in [`sdk/rust/lib/sandbox/metrics.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/metrics.rs) (lines 101-108):

```rust
// metrics.rs L101-108
pub async fn metrics(&self) -> Result<SandboxMetrics> {
    if !self.config.metrics_enabled() {
        return Err(Error::MetricsDisabled);
    }
    self.backend.metrics(&self.id).await
}

```

Example usage:

```rust
let snapshot = sandbox.metrics().await?;
println!("CPU: {:.1}%", snapshot.cpu_percent);
println!("Memory: {} MiB", snapshot.memory_bytes / 1024 / 1024);

```

This validates that metrics are enabled, then forwards to the backend implementation.

## Streaming Real-Time Metrics

For continuous monitoring, `Sandbox::metrics_stream()` creates an async stream. From [`sdk/rust/lib/sandbox/metrics.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/metrics.rs) (lines 15-26):

```rust
// metrics.rs L15-26
pub fn metrics_stream(&self, interval: Duration) -> impl Stream<Item = Result<SandboxMetrics>> {
    let backend = self.backend.clone();
    let id = self.id.clone();
    
    stream::unfold((backend, id), move |(backend, id)| {
        let interval = interval;
        async move {
            tokio::time::sleep(interval).await;
            let result = backend.metrics(&id).await;
            Some((result, (backend, id)))
        }
    })
}

```

**Key detail**: The stream interval is independent of the sandbox's configured sampling rate. You can stream every second even if the sandbox samples every 5 seconds—the backend returns the latest available sample.

Example streaming implementation:

```rust
use futures::stream::StreamExt;

let mut stream = sandbox.metrics_stream(Duration::from_secs(1));

while let Some(result) = stream.next().await {
    match result {
        Ok(m) => println!(
            "{} | CPU: {:5.1}% | Net TX: {} B",
            m.timestamp, m.cpu_percent, m.net_tx_bytes
        ),
        Err(e) => eprintln!("Metrics error: {}", e),
    }
}

```

The backend functions `local_metrics` (lines 34-55) and `local_metrics_stream` (lines 60-73) in the same file handle the actual registry reads for local backends.

## Understanding SandboxMetrics Fields

The `SandboxMetrics` struct in [`sdk/rust/lib/sandbox/metrics.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/metrics.rs) (lines 27-60) exposes these fields:

| Field | Type | Description |
|-------|------|-------------|
| `cpu_percent` | `f32` | CPU usage percentage across host CPUs |
| `vcpu_time_ns` | `u64` | Guest vCPU execution time in nanoseconds |
| `memory_bytes` | `u64` | Resident memory usage in bytes |
| `memory_available_bytes` | `Option<u64>` | Memory available to the guest |
| `memory_host_resident_bytes` | `Option<u64>` | Host-side resident set size |
| `memory_limit_bytes` | `u64` | Configured guest memory limit |
| `disk_read_bytes` | `u64` | Cumulative disk bytes read |
| `disk_write_bytes` | `u64` | Cumulative disk bytes written |
| `net_rx_bytes` | `u64` | Cumulative network bytes received |
| `net_tx_bytes` | `u64` | Cumulative network bytes transmitted |
| `uptime` | `Duration` | Time since sandbox start |
| `timestamp` | `DateTime<Utc>` | When this sample was recorded |

Use `disk_read_bytes` and `disk_write_bytes` for I/O rate calculations by comparing consecutive samples. Network counters are cumulative—divide by elapsed time for throughput rates.

## Monitoring Multiple Sandboxes

For dashboard or inventory use cases, `all_sandbox_metrics()` returns snapshots for every known sandbox. From [`sdk/rust/lib/sandbox/metrics.rs`](https://github.com/superradcompany/microsandbox/blob/main/sdk/rust/lib/sandbox/metrics.rs) (lines 212-222):

```rust
// metrics.rs L212-222
pub async fn all_sandbox_metrics() -> Result<HashMap<String, SandboxMetrics>> {
    let backend = default_backend().await?;
    let sandboxes = backend.list().await?;
    
    let mut results = HashMap::new();
    for sandbox in sandboxes {
        if let Ok(metrics) = sandbox.metrics().await {
            results.insert(sandbox.name().to_string(), metrics);
        }
    }
    Ok(results)
}

```

This iterates all sandboxes, skipping those without metrics enabled.

## CLI Commands for Resource Monitoring

The `msb` CLI exposes the same backend calls. Commands are generated from the Rust SDK in [`crates/cli/src/commands/sandbox.rs`](https://github.com/superradcompany/microsandbox/blob/main/crates/cli/src/commands/sandbox.rs).

### Single snapshot:

```bash
msb sandbox metrics <sandbox-name>

```

### Streaming with custom interval:

```bash
msb sandbox metrics-stream <sandbox-name> --interval 500ms

```

### All sandboxes summary:

```bash
msb sandbox list-metrics

```

Equivalent to the SDK's `all_sandbox_metrics()` function.

## Complete Monitoring Example

```rust
use microsandbox::{SandboxBuilder, Result};
use std::time::Duration;
use futures::stream::StreamExt;

#[tokio::main]
async fn main() -> Result<()> {
    // Create sandbox with 750ms sampling
    let sandbox = SandboxBuilder::new()
        .name("api-server")
        .image("docker.io/library/alpine:latest")
        .metrics_sample_interval(Duration::from_millis(750))
        .run()
        .await?;

    // Initial health check
    let initial = sandbox.metrics().await?;
    println!("Started with {} MiB memory", initial.memory_bytes / 1024 / 1024);

    // Monitor for 60 seconds
    let mut stream = sandbox.metrics_stream(Duration::from_secs(2));
    let start = std::time::Instant::now();
    
    while start.elapsed() < Duration::from_secs(60) {
        if let Some(Ok(m)) = stream.next().await {
            let mem_mib = m.memory_bytes / 1024 / 1024;
            let mem_pct = 100.0 * m.memory_bytes as f32 / m.memory_limit_bytes as f32;
            
            println!(
                "[{}] CPU: {:5.1}% | Mem: {:4} MiB ({:4.1}%) | Net: {} TX / {} RX",
                m.timestamp.format("%H:%M:%S"),
                m.cpu_percent, mem_mib, mem_pct,
                m.net_tx_bytes, m.net_rx_bytes
            );
        }
    }

    Ok(())
}

```

## Summary

- **Configuration**: Set `metrics_sample_interval` in `SandboxBuilder` or `SandboxConfig`; effective interval resolved via `effective_metrics_interval()` in [`config.rs`](https://github.com/superradcompany/microsandbox/blob/main/config.rs)
- **Single query**: Use `sandbox.metrics()` → `SandboxMetrics` (lines 101-108)
- **Streaming**: Use `sandbox.metrics_stream(interval)` for async iteration (lines 15-26)
- **Backend reads**: `local_metrics` and `local_metrics_stream` query the shared-memory registry (lines 34-73)
- **Batch operations**: `all_sandbox_metrics()` returns `HashMap<String, SandboxMetrics>` for all sandboxes (lines 212-222)
- **CLI**: `msb sandbox metrics`, `msb sandbox metrics-stream`, `msb sandbox list-metrics`

## Frequently Asked Questions

### How do I disable metrics collection to reduce overhead?

Set `metrics_sample_interval_ms: None` in your `SandboxConfig`, or omit the `metrics_sample_interval` call in `SandboxBuilder`. The `metrics()` and `metrics_stream()` methods will return `Error::MetricsDisabled` if you attempt to query disabled metrics.

### Can I stream metrics faster than the sandbox samples?

Yes. The stream interval is independent of the sampling rate. If you stream every 100ms but the sandbox samples every 5 seconds, you'll receive the same cached value repeatedly until a new sample is written. The backend in `local_metrics` always returns the latest available snapshot.

### What happens to metrics during a live resize?

Metrics reflect effective configuration immediately. The `local_metrics` function resolves `SandboxConfig::effective_metrics_interval()` on each call, so CPU/memory limits in the returned `SandboxMetrics` update without restarting the sandbox or monitoring client.