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

# Performance

> Choose the settings that can improve local sandbox performance

<Tooltip tip="These settings tune the local runtime. Microsandbox cloud manages its own host configuration."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

There is no single fastest setup. Start with the defaults, measure your
workload, and change one setting at a time.

The CLI and SDKs can tune individual sandboxes. Use the
[global config](/configuration) to set host-wide defaults. Sandbox YAML does
not currently expose these local performance controls.

## Baseline

Check the host and the sandbox before tuning:

```bash theme={null}
msb doctor
msb inspect my-sandbox
```

`msb doctor` reports host capabilities, including native root-disk cloning and
interrupt acceleration. `msb inspect` shows the settings the sandbox actually
uses. Replace `my-sandbox` with the sandbox you want to tune. Decide whether
you are improving creation time, workload latency, or throughput, then record
that result at your normal concurrency.

## Storage

Every OCI sandbox has a root disk. Choose its layout based on the bottleneck
you are trying to remove.

<Frame>
  <img src="https://mintcdn.com/superradcompanyinc/r29rG4N2-LJQIDzz/images/storage-layouts.svg?fit=max&auto=format&n=r29rG4N2-LJQIDzz&q=85&s=97b77ec3709787fb201cbbf5d8b9e850" alt="Layered roots share OCI image layers and add a writable layer per sandbox, while flat roots clone a materialized ext4 image into a private disk per sandbox" width="900" height="360" data-path="images/storage-layouts.svg" />
</Frame>

| | Layered (default) | Flat |
| - | - | - |
| Layout | Shared read-only image layers with a writable layer per sandbox | One ext4 disk cloned per sandbox |
| Best for | Sharing images and maximum portability | Faster creation and filesystem-heavy work |
| Disk use | Image layers are shared | Each sandbox gets a private clone |
| Patches and snapshots | Supported | Not supported |
| Host needs | None | Copy-on-write support for the fastest cloning |

Keep the layered layout unless creation time or filesystem throughput is a
measured bottleneck.

**Configure a flat root**

<CodeGroup>
  ```bash CLI theme={null}
  msb create python:3.12 --name worker --root-disk flat:8G,clone=auto
  ```

  ```typescript TypeScript theme={null}
  import { Sandbox } from "microsandbox";

  await using sb = await Sandbox.builder("worker")
    .image("python:3.12")
    .rootDisk((disk) => disk.flat().size(8192).cloneStrategy("auto"))
    .create();
  ```

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

  let sb = Sandbox::builder("worker")
      .image("python:3.12")
      .root_disk_with(|disk| disk.flat().size(8192).clone_strategy(FlatClone::Auto))
      .create()
      .await?;
  ```

  ```python Python theme={null}
  from microsandbox import FlatClone, Image, RootDisk, Sandbox

  sb = await Sandbox.create(
      "worker",
      image=Image.oci(
          "python:3.12",
          root_disk=RootDisk.flat(8192, clone=FlatClone.AUTO),
      ),
  )
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "worker",
      m.WithImage("python:3.12"),
      m.WithRootDisk(m.RootDisk.Flat(m.RootDiskFlatOptions{
          SizeMiB: 8192,
          Clone:   m.FlatCloneAuto,
      })),
  )
  ```
</CodeGroup>

To make flat roots the default for future sandboxes, set
`sandbox_defaults.oci.root_disk` in the [global config](/configuration).

**Prepare for benchmarking**

Missing flat images are materialized automatically. To exclude that one-time
work from a creation benchmark, prepare the image first:

```bash theme={null}
msb pull python:3.12 --materialize flat
```

Flat roots also have three clone strategies:

| Strategy | Behavior |
| - | - |
| `auto` | Uses a native copy-on-write clone when available, otherwise makes a sparse copy. |
| `reflink` | Requires native cloning and fails when the host cannot provide it. |
| `copy` | Always makes an independent sparse copy. |

Use `auto` unless clone support is an operational requirement.

**Verify**

Run `msb inspect worker` to see the resolved root layout and clone strategy.

See [Bootstrap](/sandboxes/bootstrap#flat-oci-rootfs) for the flat-root API and
[OCI images](/images/overview#oci-images) for image behavior.

## CPU

### Policies

Sandbox vCPU threads use the host scheduler by default. Linux and Windows can
also place those threads according to a policy.

| Policy | Behavior | Use when |
| - | - | - |
| `inherit` (default) | Leaves placement to the host scheduler. | You do not need managed placement. |
| `auto` | Uses available physical cores first, then shares CPUs. | You run a dedicated sandbox host. |
| `spread` | Spreads work across physical cores. | CPU throughput matters most. |
| `compact` | Packs work onto fewer physical cores. | Cache locality or host density matters most. |

**Configure placement**

<CodeGroup>
  ```bash CLI theme={null}
  msb create python:3.12 --name worker --cpus 2 --cpu-placement spread
  ```

  ```typescript TypeScript theme={null}
  import { Sandbox } from "microsandbox";

  await using sb = await Sandbox.builder("worker")
    .image("python:3.12")
    .cpus(2)
    .cpuPlacement("spread")
    .create();
  ```

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

  let sb = Sandbox::builder("worker")
      .image("python:3.12")
      .cpus(2)
      .cpu_placement(CpuPlacement::Spread)
      .create()
      .await?;
  ```

  ```python Python theme={null}
  from microsandbox import CpuPlacement, Sandbox

  sb = await Sandbox.create(
      "worker",
      image="python:3.12",
      cpus=2,
      cpu_placement=CpuPlacement.SPREAD,
  )
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "worker",
      m.WithImage("python:3.12"),
      m.WithCPUs(2),
      m.WithCPUPlacement(m.CPUPlacementSpread),
  )
  ```
</CodeGroup>

Set `sandbox_defaults.cpu_placement` in the
[global config](/configuration) to choose a host-wide default.

### NUMA

On a multi-node host, a placement profile can keep a sandbox's CPU and memory
on one NUMA node. Define the profile in the [global config](/configuration):

```json theme={null}
{
  "runtime": {
    "placement_profiles": {
      "latency": {
        "numa": { "mode": "prefer_single" },
        "memory": { "mode": "follow_cpu" }
      }
    }
  }
}
```

Then select it when creating the sandbox:

<CodeGroup>
  ```bash CLI theme={null}
  msb create python:3.12 \
    --name worker \
    --cpus 2 \
    --cpu-placement auto \
    --placement-profile latency
  ```

  ```typescript TypeScript theme={null}
  import { Sandbox } from "microsandbox";

  await using sb = await Sandbox.builder("worker")
    .image("python:3.12")
    .cpus(2)
    .cpuPlacement("auto")
    .placementProfile("latency")
    .create();
  ```

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

  let sb = Sandbox::builder("worker")
      .image("python:3.12")
      .cpus(2)
      .cpu_placement(CpuPlacement::Auto)
      .placement_profile("latency")
      .create()
      .await?;
  ```

  ```python Python theme={null}
  from microsandbox import CpuPlacement, Sandbox

  sb = await Sandbox.create(
      "worker",
      image="python:3.12",
      cpus=2,
      cpu_placement=CpuPlacement.AUTO,
      placement_profile="latency",
  )
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "worker",
      m.WithImage("python:3.12"),
      m.WithCPUs(2),
      m.WithCPUPlacement(m.CPUPlacementAuto),
      m.WithPlacementProfile("latency"),
  )
  ```
</CodeGroup>

Use `prefer_single` as an optimization: it falls back to normal placement when
one node cannot fit the sandbox. Use `strict_single` when the workload should
not start without single-node placement.

**Verify**

Run `msb inspect worker` to see the resolved policy, profile, and assigned host
CPUs.

### Caveats

* Placement coordinates only sandboxes that share the same `MSB_HOME`.
* It considers the sandbox's maximum CPU count, including CPUs currently offline.
* Normal policies may share logical CPUs when exclusive capacity runs out.
* Placement does not isolate host processes or reserve dedicated cores.
* macOS falls back to `inherit` because public APIs do not provide hard CPU affinity.
* Normal policies fall back to `inherit` when placement fails;
  `strict_single` fails the sandbox instead.

## Memory

Transparent huge pages (THP) control how the guest handles large memory
mappings.

| Policy | Use when |
| - | - |
| `madvise` (default) | You want the safe default. |
| `always` | Benchmarks show a gain for large, sustained mappings. |
| `never` | Predictable small-page behavior matters more. |

THP changes apply on the next boot. Keep `madvise` unless workload tests show
a clear gain.

**Configure THP**

<CodeGroup>
  ```bash CLI theme={null}
  msb create python:3.12 --name memory-worker --thp always
  ```

  ```typescript TypeScript theme={null}
  import { Sandbox } from "microsandbox";

  await using sb = await Sandbox.builder("memory-worker")
    .image("python:3.12")
    .thp("always")
    .create();
  ```

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

  let sb = Sandbox::builder("memory-worker")
      .image("python:3.12")
      .thp(TransparentHugePagePolicy::Always)
      .create()
      .await?;
  ```

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

  sb = await Sandbox.create(
      "memory-worker",
      image="python:3.12",
      thp="always",
  )
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "memory-worker",
      m.WithImage("python:3.12"),
      m.WithTHP(m.THPAlways),
  )
  ```
</CodeGroup>

Set `sandbox_defaults.thp` in the [global config](/configuration) to choose a
host-wide default. The policy cannot change with `msb modify`; create or
replace the sandbox to select another value.

**Verify**

Run `msb inspect memory-worker` to see the resolved THP policy.

See [Bootstrap](/sandboxes/bootstrap#transparent-huge-pages) for how THP is
applied and [Live modify](/sandboxes/tuning) for memory sizing and resize
headroom.

## Writeback

On Linux, block writeback limits the buffered disk data that active sandboxes
can hold in host memory.

* `auto` derives safe limits from the host and is the recommended setting.
* `fixed` uses operator-defined limits. Choose it only after measuring a
  representative workload.
* `off` removes the limit for new sandboxes, allowing buffered guest writes to
  create more host-memory pressure.

**Configure writeback**

Writeback is global runtime policy, not a per-sandbox SDK option:

```json theme={null}
{
  "runtime": {
    "block_writeback": {
      "mode": "auto"
    }
  }
}
```

Writeback does not affect non-Linux hosts, read-only disks, or direct I/O. See
[Global config](/configuration) for `fixed` limits and pool settings.

## Host

On Linux x86, AMD AVIC and Intel APICv can accelerate virtual interrupts. These
are host-wide KVM policies, not sandbox settings. `msb doctor` reports their
status without changing them.

The same command probes whether `MSB_HOME` supports native flat-root clones.
A copy fallback is correct but slower to provision.

Read [Linux troubleshooting](/troubleshooting/linux#interrupt-acceleration)
before changing a KVM module because the change affects every VM on the host.

## Measure

* Cache or materialize the same image before each test unless cold-start cost
  is what you are measuring.
* Keep CPU, memory, storage, host power settings, and concurrency unchanged.
* Measure sandbox creation separately from the workload.
* Repeat each test and compare the median, not a single run.
* Use `msb inspect` to confirm the intended setting was applied.


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