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

# Live Modify

> Resize a running sandbox and update its configuration without replacing it

<Tooltip tip="modify and live resize are not yet available on microsandbox cloud; create a replacement sandbox with the new configuration."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

You do not need to replace a running sandbox just because its workload changes. `modify` can resize the VM, update host-side metadata, rotate existing secrets, and change the defaults used by future commands while the sandbox stays up.

<span id="what-can-modify-change" />

## Supported changes

When a change takes effect depends on the setting:

| Change | When it takes effect | Notes |
| - | - | - |
| CPU and memory | Live, within the boot-time `max_*` ceilings | The VM and workload keep running |
| Labels | Live | Host-side metadata; guest processes do not change |
| Existing secret material or removal | Live | Adding a secret or changing its placeholder needs a restart |
| Environment and workdir | Future execs | Processes that are already running keep their current values |
| `max_cpus` and `max_memory` | Restart or next start | These ceilings are fixed when the VM boots |
| Root disk size | Live, restart or next start | Managed and flat ext4 roots grow only; tmpfs changes on boot |
| Named volumes, mount tmpfs, and user disk images | Outside `modify` | Capacity is managed where the storage is defined |

The default policy applies only changes that can complete without restarting. If a patch contains one restart-required change, microsandbox rejects the whole patch and the old configuration stays intact.

<span id="modify-a-running-sandbox" />

## Apply changes

This patch doubles the running sandbox's CPU and memory and updates a label in the same operation:

<CodeGroup>
  ```bash CLI theme={null}
  msb modify worker --cpus 4 --memory 2G --label tier=web
  ```

  ```typescript TypeScript theme={null}
  const plan = await sandbox.modify({
    cpus: 4,
    memory: 2048,
    labels: { tier: "web" },
  });
  ```

  ```rust Rust theme={null}
  let plan = sb.modify()
      .cpus(4)
      .memory(2048)
      .label("tier", "web")
      .apply()
      .await?;
  ```

  ```python Python theme={null}
  plan = await sb.modify(
      cpus=4,
      memory=2048,
      labels={"tier": "web"},
  )
  ```

  ```go Go theme={null}
  plan, err := sb.Modify(ctx, m.ModifyOptions{
      CPUs:      4,
      MemoryMiB: 2048,
      Labels:    map[string]string{"tier": "web"},
  })
  ```
</CodeGroup>

The result is a modification plan showing what changed and whether each change was applied. CPU and memory can take a moment to converge inside the guest. The new host limits take effect immediately, and the workload continues running.

<span id="reserve-resize-headroom" />

## Set resize limits

Live growth needs capacity reserved when the VM boots. Set `max_cpus` and `max_memory` above the starting allocation when you create a sandbox that may need to scale:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await using sandbox = await Sandbox.builder("worker")
    .image("python")
    .cpus(2)
    .memory(1024)
    .maxCpus(8)
    .maxMemory(4096)
    .create();
  ```

  ```rust Rust theme={null}
  let sb = Sandbox::builder("worker")
      .image("python")
      .cpus(2)
      .memory(1024)
      .max_cpus(8)
      .max_memory(4096)
      .create()
      .await?;
  ```

  ```python Python theme={null}
  sb = await Sandbox.create(
      "worker",
      image="python",
      cpus=2,
      memory=1024,
      max_cpus=8,
      max_memory=4096,
  )
  ```

  ```go Go theme={null}
  sb, err := m.CreateSandbox(ctx, "worker",
      m.WithImage("python"),
      m.WithCPUs(2),
      m.WithMemory(1024),
      m.WithMaxCPUs(8),
      m.WithMaxMemory(4096),
  )
  ```

  ```ruby Ruby theme={null}
  sb = Microsandbox::Sandbox.create(
    "worker",
    image: "python",
    cpus: 2,
    memory: 1024,
    max_cpus: 8,
    max_memory: 4096
  )
  ```

  ```bash CLI theme={null}
  msb create python --name worker \
    --cpus 2 --memory 1G \
    --max-cpus 8 --max-memory 4G
  ```
</CodeGroup>

The sandbox starts with two vCPUs and 1 GiB of memory, but it can grow live to eight vCPUs and 4 GiB. The ceilings default to the starting size, so omitting them leaves no live growth headroom.

Reserving headroom is cheap: spare vCPUs stay parked, and microsandbox backs spare memory only when the guest uses it. You can lower CPU or memory live without pre-planning, then grow back up to the booted ceiling later.

<span id="preview-before-applying" />

## Preview changes

Use a dry run when a patch mixes settings or you are unsure whether a restart is needed:

<CodeGroup>
  ```bash CLI theme={null}
  msb modify worker --max-memory 16G --dry-run
  ```

  ```typescript TypeScript theme={null}
  const plan = await sandbox.modify({
    maxMemory: 16_384,
    dryRun: true,
  });
  ```

  ```rust Rust theme={null}
  let plan = sb.modify()
      .max_memory(16_384)
      .dry_run()
      .await?;
  ```

  ```python Python theme={null}
  plan = await sb.modify(max_memory=16_384, dry_run=True)
  ```

  ```go Go theme={null}
  plan, err := sb.Modify(ctx, m.ModifyOptions{
      MaxMemoryMiB: 16_384,
      DryRun:        true,
  })
  ```
</CodeGroup>

The planner classifies every requested field before it changes anything:

| Disposition | Meaning |
| - | - |
| `live` | Applies without restarting the VM |
| `next start` | Is saved and applies the next time the sandbox starts |
| `requires restart` | Cannot affect the running VM under the default policy |
| `unsupported` | Is invalid for this sandbox or backing type |

<span id="live-paths-in-detail" />

## Update settings

<span id="cpu-and-memory" />

### Resize CPU and memory

<Frame>
  <img src="https://mintcdn.com/superradcompanyinc/uksi6OYeD2cA6qdw/images/live-modify.svg?fit=max&auto=format&n=uksi6OYeD2cA6qdw&q=85&s=c1516b6f3c41ca60464f5c14899a8617" alt="A terminal changes CPU, memory, and a label while the sandbox remains running" width="680" height="240" data-path="images/live-modify.svg" />
</Frame>

Raise or lower CPU and memory while the workload runs:

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

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

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

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

  ```go Go theme={null}
  plan, err := sb.Modify(ctx, m.ModifyOptions{
      CPUs:      4,
      MemoryMiB: 2048,
  })
  ```
</CodeGroup>

Use [`msb ps`](/cli/sandbox-commands#msb-status) to see allocation as `effective / max`, and [`msb metrics`](/cli/sandbox-commands#msb-metrics) to check real usage before resizing. The apply result reports `applied`, `converging`, `guest-refused`, or `failed` for each resource so automation can wait for the guest to settle.

<span id="labels" />

### Update labels

Labels are host-side metadata, so adding, changing, or removing one is immediate:

<CodeGroup>
  ```bash CLI theme={null}
  msb modify worker --label tier=web --label-rm stale
  ```

  ```typescript TypeScript theme={null}
  const plan = await sandbox.modify({
    labels: { tier: "web" },
    labelsRemove: ["stale"],
  });
  ```

  ```rust Rust theme={null}
  let plan = sb.modify()
      .label("tier", "web")
      .remove_label("stale")
      .apply()
      .await?;
  ```

  ```python Python theme={null}
  plan = await sb.modify(
      labels={"tier": "web"},
      labels_rm=["stale"],
  )
  ```

  ```go Go theme={null}
  plan, err := sb.Modify(ctx, m.ModifyOptions{
      Labels:       map[string]string{"tier": "web"},
      LabelsRemove: []string{"stale"},
  })
  ```
</CodeGroup>

Running guest processes do not change. The new labels are available to listing, selection, metrics attribution, and other host-side workflows. See [Labels](/sandboxes/labels) for naming and cardinality guidance.

<span id="environment-and-workdir" />

### Set command defaults

Environment and workdir updates require no restart, but they affect only commands started after the patch:

<CodeGroup>
  ```bash CLI theme={null}
  msb modify worker --env MODE=prod --workdir /app
  msb exec worker -- printenv MODE
  ```

  ```typescript TypeScript theme={null}
  await sandbox.modify({
    env: { MODE: "prod" },
    workdir: "/app",
  });

  const output = await sandbox.exec("printenv", ["MODE"]);
  ```

  ```rust Rust theme={null}
  sb.modify()
      .env("MODE", "prod")
      .workdir("/app")
      .apply()
      .await?;

  let output = sb.exec("printenv", ["MODE"]).await?;
  ```

  ```python Python theme={null}
  await sb.modify(env={"MODE": "prod"}, workdir="/app")
  output = await sb.exec("printenv", ["MODE"])
  ```

  ```go Go theme={null}
  _, err := sb.Modify(ctx, m.ModifyOptions{
      Env:     map[string]string{"MODE": "prod"},
      Workdir: "/app",
  })
  out, err := sb.Exec(ctx, "printenv", []string{"MODE"})
  ```
</CodeGroup>

Processes that were already running keep their original environment and working directory.

<span id="secrets" />

### Rotate secrets

Rotating the value of an existing secret is live because substitution happens at the host network boundary. Guest code keeps using the same placeholder while microsandbox begins injecting the new value:

<CodeGroup>
  ```bash CLI theme={null}
  msb modify worker --secret GITHUB_TOKEN@api.github.com
  ```

  ```typescript TypeScript theme={null}
  const plan = await sandbox.modify({
    secrets: {
      GITHUB_TOKEN: {
        env: "GITHUB_TOKEN",
        allowedHosts: ["api.github.com"],
      },
    },
  });
  ```

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

  let plan = sb.modify()
      .secret(|secret| secret
          .env("GITHUB_TOKEN")
          .source(SecretSource::Env { var: "GITHUB_TOKEN".into() })
          .allow("api.github.com"))
      .remove_secret("OLD_TOKEN")
      .apply()
      .await?;
  ```

  ```python Python theme={null}
  plan = await sb.modify(
      secrets={
          "GITHUB_TOKEN": {
              "env": "GITHUB_TOKEN",
              "allowed_hosts": ["api.github.com"],
          },
      },
  )
  ```

  ```go Go theme={null}
  plan, err := sb.Modify(ctx, m.ModifyOptions{
      Secrets: map[string]m.SecretModifySpec{
          "GITHUB_TOKEN": {
              Env:          "GITHUB_TOKEN",
              AllowedHosts: []string{"api.github.com"},
          },
      },
  })
  ```
</CodeGroup>

Adding a new secret or changing its guest-visible placeholder requires a restart because existing processes cannot receive a new placeholder. If the new secret requires TLS identity while interception is off, the plan also enables `tls`, which needs the same restart or next start. Existing secrets configured with `require_tls_identity(false)` continue to rotate live over plain HTTP without enabling interception. Removing a secret does not require recreating the sandbox, and removing every TLS-dependent secret leaves interception on. See [Secrets](/sandboxes/secrets) for sources, host allow lists, and storage behavior.

<span id="root-disk-growth" />

### Grow the root disk

Grow an owned managed or flat ext4 root without restarting, including roots backed by checkpoint layers:

<CodeGroup>
  ```bash CLI theme={null}
  msb modify worker --root-disk 8G
  ```

  ```typescript TypeScript theme={null}
  await sandbox.modify({ rootDiskSize: 8192 });
  ```

  ```rust Rust theme={null}
  sb.modify().root_disk_size(8192).apply().await?;
  ```

  ```python Python theme={null}
  await sb.modify(root_disk_size=8192)
  ```

  ```go Go theme={null}
  _, err := sb.Modify(ctx, m.ModifyOptions{RootDiskSizeMiB: 8192})
  ```
</CodeGroup>

Microsandbox briefly pauses the VM to extend its writable disk, resumes it, then asks ext4 to make the space usable. Existing snapshots and sealed backing layers stay unchanged. A stopped sandbox grows before the new size is persisted.

Growth is explicit and grow-only. The ext4 image must have enough reserved metadata headroom; older runtimes and filesystems without online-resize support return an error instead of silently restarting. User-supplied disks and cloud sandboxes remain unsupported. Use `--next-start` to defer growth or `--restart` to use stopped growth.

If the disk grows but guest filesystem expansion fails, the error reports incomplete growth. Retry the same target, or stop and restart to finish recovery. Microsandbox never truncates the disk to roll back; snapshots and compaction stay blocked until recovery completes.

<span id="changes-that-need-a-boot-boundary" />

## Restart required

These changes take effect after a restart:

| Change | Apply now | Defer safely |
| - | - | - |
| Raise `max_cpus` or `max_memory` | `--restart` | `--next-start` |
| Resize a tmpfs root disk | On restart | `--next-start` |
| Add a new secret or change its placeholder | `--restart` | `--next-start` |

<CodeGroup>
  ```bash CLI theme={null}
  msb modify worker --max-memory 16G --next-start
  msb modify worker --root-disk 8G --restart
  ```

  ```typescript TypeScript theme={null}
  await sandbox.modify({
    maxMemory: 16_384,
    policy: "next_start",
  });

  await sandbox.modify({
    rootDiskSize: 8192,
    policy: "restart",
  });
  ```

  ```rust Rust theme={null}
  sb.modify()
      .max_memory(16_384)
      .next_start()
      .apply()
      .await?;

  sb.modify()
      .root_disk_size(8192)
      .restart()
      .apply()
      .await?;
  ```

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

  await sb.modify(
      max_memory=16_384,
      policy=ModificationPolicy.NEXT_START,
  )

  await sb.modify(
      root_disk_size=8192,
      policy=ModificationPolicy.RESTART,
  )
  ```

  ```go Go theme={null}
  _, err := sb.Modify(ctx, m.ModifyOptions{
      MaxMemoryMiB: 16_384,
      Policy:       m.ModificationPolicyNextStart,
  })

  _, err = sb.Modify(ctx, m.ModifyOptions{
      RootDiskSizeMiB: 8192,
      Policy:          m.ModificationPolicyRestart,
  })
  ```
</CodeGroup>

`--next-start` saves the desired configuration without touching the running VM. `--restart` stops and starts the sandbox only when the patch needs it. The default policy does neither and rejects restart-required changes.

Root disk resizing remains conservative: managed and flat OCI disks grow only, tmpfs can grow or shrink at the next boot, and user-supplied disk images are never resized by microsandbox. Named volume and mount capacity is managed where that storage is defined. See [Volumes](/sandboxes/volumes) and [Images](/images/overview) for the storage model.

## Reference

For every CLI flag and result state, see [`msb modify`](/cli/sandbox-commands#msb-modify). The SDK sandbox references expose the same planner and policies for [TypeScript](/sdk/typescript/sandbox), [Rust](/sdk/rust/sandbox), [Python](/sdk/python/sandbox), and [Go](/sdk/go/sandbox).


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