- name
- sandbox
- description
- Creates isolated Linux MicroVMs using Vercel Sandbox SDK. Use when building code execution environments, running untrusted code, spinning up dev servers, testing in isolation, or when the user mentions "sandbox", "microvm", "isolated execution", or "@vercel/sandbox".
- metadata
- {"author":"Vercel Inc.","version":"2.0"}
## _CRITICAL_: Always Use Correct `@vercel/sandbox` Documentation
Your knowledge of `@vercel/sandbox` may be outdated.
Follow these instructions before starting on any sandbox-related tasks:
### Official Resources
- **Documentation**: https://vercel.com/docs/vercel-sandbox
- **SDK Reference**: https://vercel.com/docs/vercel-sandbox/sdk-reference
- **CLI Reference**: https://vercel.com/docs/vercel-sandbox/cli-reference
- **GitHub**: https://github.com/vercel/sandbox
- **REST API**: https://vercel.com/docs/rest-api/sandboxes
### What changed in v2
The `@vercel/sandbox@2` SDK and `sandbox@3` CLI replace anonymous, ephemeral
sandboxes with **named, persistent** sandboxes. Key differences from v1:
- Sandboxes are identified by **`name`** (not `sandboxId`). Names are unique per project.
- Sandboxes are **persistent by default** — when a sandbox stops, the SDK
automatically snapshots it and restores the filesystem on the next resume.
- A **Session** is a single running VM instance inside a sandbox. SDK calls
like `runCommand`, `writeFiles`, etc. automatically resume a stopped sandbox.
- New methods: `Sandbox.getOrCreate`, `Sandbox.fork`, `sandbox.update`,
`sandbox.delete`, `sandbox.listSessions`, `sandbox.listSnapshots`,
`Snapshot.tree`, `defineSandboxProxy`.
- New params: `name`, `persistent`, `tags`, `onResume`, `snapshotExpiration`,
`keepLastSnapshots`, L7 network policy matchers, `forwardURL`.
- Pagination uses **cursor-based** iterators (async-iterable) instead of `since`/`until`.
- `Sandbox.list({ since, until })` → `Sandbox.list({ cursor, namePrefix, sortBy, tags })`.
- v1 sandboxes are backfilled so the only required code change is using `name`
instead of `sandboxId`.
### Quick Reference
**Essential imports:**
```typescript
// Core SDK
import {
Sandbox,
Session,
Snapshot,
Command,
CommandFinished,
} from "@vercel/sandbox";
import { APIError, StreamError } from "@vercel/sandbox";
// For advanced network policy with credential brokering and L7 matchers
import type {
NetworkPolicy,
NetworkPolicyRule,
NetworkTransformer,
} from "@vercel/sandbox";
// For implementing a request-forwarding proxy (forwardURL)
import { defineSandboxProxy } from "@vercel/sandbox/proxy";
// For timeouts
import ms from "ms"; // e.g., ms("5m"), ms("1h")
```
**Default image:** Sandboxes use `vercel/sandbox/universal:latest`, an
Ubuntu-based image with Node.js 24, Bun, Python 3.14, coding agents, and common
development and debugging utilities.
## Creating Sandboxes
### Basic Creation
```typescript
import { Sandbox } from "@vercel/sandbox";
const sandbox = await Sandbox.create({
name: "my-dev-env", // Optional, random if omitted. Unique per project.
resources: { vcpus: 4 }, // 2048 MB RAM per vCPU
ports: [3000], // Expose up to 15 ports
timeout: ms("10m"), // Default: 5 minutes
env: { NODE_ENV: "production" }, // Env vars inherited by all commands
tags: { env: "staging", team: "infra" }, // Up to 5 key:value tags
persistent: true, // Default: true. Auto-snapshots on stop, restores on resume.
snapshotExpiration: ms("7d"), // Default TTL for snapshots. Use 0 for no expiration.
region: "<region>", // Optional, defaults to iad1. Any Vercel region, e.g. sfo1, fra1, hnd1, syd1.
failoverRegions: ["<region>"], // Optional, e.g. ["sfo1", "fra1"]. Must not include `region`.
});
console.log(sandbox.name);
```
### Retrieve an Existing Sandbox
```typescript
// Retrieve by name. The sandbox will resume automatically the next time
// you run a command.
const sandbox = await Sandbox.get({ name: "my-dev-env" });
```
### Get-or-Create (Idempotent)
`Sandbox.getOrCreate` is the recommended pattern for long-lived sandboxes.
```typescript
const sandbox = await Sandbox.getOrCreate({
name: "my-workspace",
// Runs only the first time the sandbox is created.
onCreate: async (sbx) => {
await sbx.writeFiles([
{ path: "README.md", content: Buffer.from("# Hello") },
]);
await sbx.runCommand("npm", ["install"]);
},
// Runs every time the sandbox session is resumed (including after auto-resume).
onResume: async (sbx) => {
await sbx.runCommand({ cmd: "npm", args: ["run", "dev"], detached: true });
},
});
```
Behavior:
- If a sandbox with that `name` exists → resumes it and fires `onResume`.
- If it does not exist → creates a fresh sandbox and fires `onCreate`.
- If the sandbox exists but its snapshot expired → deletes the stale sandbox,
re-creates it with the same name, and fires `onCreate`.
### Re-warming on Resume
Use `onResume` to restart background services or rehydrate caches whenever a
persistent sandbox's session is resumed:
```typescript
const sandbox = await Sandbox.get({
name: "my-workspace",
onResume: async (sbx) => {
await sbx.runCommand({ cmd: "npm", args: ["run", "dev"], detached: true });
},
});
```
`onResume` also fires when a SDK call (e.g. `runCommand`, `writeFiles`)
auto-resumes a stopped sandbox.
### With Git Source
```typescript
const sandbox = await Sandbox.create({
source: {
type: "git",
url: "https://github.com/vercel/sandbox-example-next.git",
depth: 1, // Shallow clone (optional)
revision: "main", // Branch, tag, or commit (optional)
},
ports: [3000],
});
```
Git sources are cloned into a subdirectory named after the repository. For the
example above, run commands with `cwd: "sandbox-example-next"`.
### With Private Git Repository
```typescript
const sandbox = await Sandbox.create({
source: {
type: "git",
url: "https://github.com/org/private-repo.git",
username: process.env.GIT_USERNAME!,
password: process.env.GIT_TOKEN!, // Use PAT for password
},
});
```
### From Tarball
```typescript
const sandbox = await Sandbox.create({
source: {
type: "tarball",
url: "https://example.com/project.tar.gz",
},
ports: [3000],
});
```
### From a Snapshot
```typescript
const sandbox = await Sandbox.create({
source: {
type: "snapshot",
snapshotId: "snap_abc123",
},
ports: [3000],
});
```
### From a Custom Image (VCR)
Override the default with a [Vercel Container
Registry](https://vercel.com/docs/container-registry) (VCR) image stored in the
sandbox's project.
For a different distro or reproducible system tooling, prefer baking it into a
custom image over installing packages when the sandbox starts.
```typescript
const sandbox = await Sandbox.create({
image: "my-repo:v1",
ports: [3000],
});
```
`image` accepts a repository in the sandbox's project with an optional tag or
digest, or a fully-qualified VCR URL. A bare repository name resolves to the
`latest` tag:
```typescript
await Sandbox.create({ image: "my-repo" }); // latest tag
await Sandbox.create({ image: "my-repo:v1" }); // specific tag
await Sandbox.create({ image: "my-repo@sha256:..." }); // specific digest
await Sandbox.create({
image: "vcr.vercel.com/my-team/my-project/my-repo:v1", // fully-qualified
});
```
Push images to VCR with Docker-compatible tooling before referencing them. See the
[Container Registry docs](https://vercel.com/docs/container-registry) for push
instructions.
### Forking an Existing Sandbox
`Sandbox.fork` seeds a new sandbox from another sandbox's current snapshot
and copies its config (`resources`, `timeout`, `networkPolicy`, `tags`,
`ports`, `image`, `persistent`, `snapshotExpiration`, `keepLastSnapshots`,
and `env`). Any field you pass overrides the inherited value. If the source
has no current snapshot, the fork falls back to the source's base environment
plus the copied config. You can only fork a sandbox in a project you have
access to; forking an unknown source returns a 404. The fork runs in the
source's region unless you pass `region` (and optionally `failoverRegions`)
to override it.
```typescript
// Inherit everything from the source (env included)
const fork = await Sandbox.fork({ sourceSandbox: "prod-agent" });
// Override specific fields; the rest are copied from the source.
// A provided `env` fully replaces the source's env (no per-key merge).
const fork = await Sandbox.fork({
sourceSandbox: "prod-agent",
name: "forked-prod-agent",
resources: { vcpus: 4 },
env: { OPENAI_API_KEY: process.env.OPENAI_API_KEY! },
});
```
### Auto-Dispose Pattern
Use `await using` for automatic cleanup:
```typescript
async function runInSandbox() {
await using sandbox = await Sandbox.create();
// Sandbox automatically stopped when scope exits
await sandbox.runCommand("echo", ["Hello"]);
}
```
## Running Commands
### Basic Command Execution
```typescript
const result = await sandbox.runCommand("npm", ["install"]);
if (result.exitCode !== 0) {
console.error("Install failed:", await result.stderr());
}
```
### With Options
```typescript
const result = await sandbox.runCommand({
cmd: "npm",
args: ["run", "build"],
cwd: "/vercel/sandbox/app",
env: { NODE_ENV: "production" },
sudo: false,
stdout: process.stdout, // Stream output
stderr: process.stderr,
});
```
### Detached Commands (Background Processes)
```typescript
// Start dev server in background
const devServer = await sandbox.runCommand({
cmd: "npm",
args: ["run", "dev"],
detached: true, // Returns immediately
stdout: process.stdout,
});
// Later: wait for completion or kill
const finished = await devServer.wait();
// Supported signals: SIGHUP, SIGINT, SIGQUIT, SIGKILL, SIGTERM, SIGCONT, SIGSTOP (or numeric)
await devServer.kill("SIGTERM");
```
### Root Access
```typescript
await sandbox.runCommand({
cmd: "apt-get",
args: ["update"],
sudo: true,
});
await sandbox.runCommand({
cmd: "apt-get",
args: ["install", "-y", "golang-go"],
sudo: true, // Execute as root
});
```
## File Operations
### Write Files
```typescript
await sandbox.writeFiles([
{
path: "/vercel/sandbox/config.json",
content: Buffer.from(JSON.stringify({ key: "value" })),
},
{
path: "/vercel/sandbox/script.sh",
content: Buffer.from("#!/bin/bash\necho 'Hello'"),
mode: 0o755,
},
]);
```
### Read Files
```typescript
// Returns a Buffer object
const buffer = await sandbox.readFileToBuffer({
path: "/vercel/sandbox/output.txt",
});
// Returns a NodeJS.ReadableStream
const stream = await sandbox.readFile({
path: "/vercel/sandbox/large-file.bin",
});
```
### Download Files
```typescript
const localPath = await sandbox.downloadFile(
{ path: "/vercel/sandbox/report.pdf" }, // source path on the sandbox
{ path: "./downloads/report.pdf" }, // destination path on the local machine
{ mkdirRecursive: true },
);
```
### Create Directories
```typescript
await sandbox.mkDir("/vercel/sandbox/my-app/src");
```
### `sandbox.fs` — `node:fs/promises`-compatible API
```typescript
const content = await sandbox.fs.readFile("/etc/hostname", "utf8");
await sandbox.fs.writeFile("/tmp/hello.txt", "Hello, world!");
View on GitHub