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".
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:
v1 sandboxes are backfilled so the only required code change is using name
instead of sandboxId.
Quick Reference
Essential imports:
// Core SDKimport {
Sandbox,
Session,
Snapshot,
Command,
CommandFinished,
} from"@vercel/sandbox";
import { APIError, StreamError } from"@vercel/sandbox";
// For advanced network policy with credential brokering and L7 matchersimporttype {
NetworkPolicy,
NetworkPolicyRule,
NetworkTransformer,
} from"@vercel/sandbox";
// For implementing a request-forwarding proxy (forwardURL)import { defineSandboxProxy } from"@vercel/sandbox/proxy";
// For timeoutsimport 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
import { Sandbox } from"@vercel/sandbox";
const sandbox = awaitSandbox.create({
name: "my-dev-env", // Optional, random if omitted. Unique per project.resources: { vcpus: 4 }, // 2048 MB RAM per vCPUports: [3000], // Expose up to 15 portstimeout: ms("10m"), // Default: 5 minutesenv: { NODE_ENV: "production" }, // Env vars inherited by all commandstags: { env: "staging", team: "infra" }, // Up to 5 key:value tagspersistent: 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. See the Vercel docs for available regions.failoverRegions: ["<region>"], // Optional. Must not include `region`.
});
console.log(sandbox.name);
Retrieve an Existing Sandbox
// Retrieve by name. The sandbox will resume automatically the next time// you run a command.const sandbox = awaitSandbox.get({ name: "my-dev-env" });
Get-or-Create (Idempotent)
Sandbox.getOrCreate is the recommended pattern for long-lived sandboxes.
const sandbox = awaitSandbox.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:
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:
Push images to VCR with Docker-compatible tooling before referencing them. See the
Container Registry docs 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.
// Inherit everything from the source (env included)const fork = awaitSandbox.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 = awaitSandbox.fork({
sourceSandbox: "prod-agent",
name: "forked-prod-agent",
resources: { vcpus: 4 },
env: { OPENAI_API_KEY: process.env.OPENAI_API_KEY! },
});
Create isolated Linux users and shared groups inside a sandbox. This is purely
SDK-side and is useful for isolating multiple agents or workloads within a single
sandbox. Usernames and group names are validated to prevent command injection.
Creating Users
createUser provisions a Linux user with an isolated home directory at
/home/<username> and returns a SandboxUser whose operations run in that
user's context.
Files are isolated between users — one user cannot read, list, or write another
user's home directory (commands return a non-zero exit code with "Permission
denied").
Groups and Shared Directories
createGroup creates a Linux group with a shared directory at
/shared/<groupname> (setgid 2770), so files created inside it automatically
inherit group ownership. Group members can read and write there; non-members
are blocked.
const devs = await sandbox.createGroup("devs");
devs.sharedDir; // "/shared/devs"await sandbox.addUserToGroup("alice", "devs");
await sandbox.addUserToGroup("bob", "devs");
// Or via convenience methods on SandboxUserawait alice.addToGroup("devs");
await alice.removeFromGroup("devs");
await sandbox.removeUserFromGroup("alice", "devs");
Rules can match on method, path, query string, and headers. All specified
dimensions must match; multiple methods are ORed; multiple header and
query-string matchers are ANDed.
Matchers support exact, startsWith, and regex (RE2).
Forward Matching Requests to a Proxy
Use forwardURL to redirect any matching request through an HTTPS proxy you
control. The proxy receives the original request along with sandbox metadata in
forwarded headers.
Implement the proxy handler with defineSandboxProxy, using the Web Request & Response objects — it verifies the
sandbox OIDC token and extracts metadata about the source sandbox:
// app/api/sandbox-proxy/route.tsimport { defineSandboxProxy } from"@vercel/sandbox/proxy";
const handler = defineSandboxProxy(async (request, meta) => {
// meta: { host, teamId, projectId, sandboxId, sandboxName }console.log("Proxied from sandbox", meta.sandboxName);
returnfetch(request);
});
// Sandboxes forward requests using their original method, so the handler// must be exposed under every verb the network policy can route.export {
handler asGET,
handler asPOST,
handler asPUT,
handler asPATCH,
handler asDELETE,
};
Updating Network Policy at Runtime
Use sandbox.update (preferred). updateNetworkPolicy is deprecated but still
works.
sandbox.update replaces individual update helpers and accepts any of the
mutable parameters. When ports is provided, it is treated as the full
desired port list — any currently exposed port not present in the array is
deregistered.
await sandbox.update({
resources: { vcpus: 4 }, // Memory auto-scales to 2048 MB per vCPUtimeout: ms("30m"),
networkPolicy: "deny-all",
ports: [3000, 8000],
tags: { env: "prod" },
persistent: false,
snapshotExpiration: ms("14d"),
keepLastSnapshots: { count: 1 },
currentSnapshotId: "snap_xyz", // Rollback to a previous snapshotregion: "sfo1", // Applies to the next sessionfailoverRegions: ["cle1"], // Replaces the list; pass [] to remove them
});
region and failoverRegions can be updated independently, but failoverRegions must not include the configured region.
Deleting a Sandbox
// Permanently remove a sandbox. Its snapshots are kept until they expire.await sandbox.delete();
// Also delete the snapshots that no other sandbox uses.await sandbox.delete({ deleteOrphanSnapshots: true });
Stopping a Sandbox
stop() is synchronous: it blocks until the VM is fully stopped and returns
the final session state, including the snapshot created during shutdown (when
persistent: true).
All list APIs use cursor-based pagination and return an async-iterable that
auto-paginates through every page. You can also iterate page-by-page or
collect all items at once.
Sandbox.list
const result = awaitSandbox.list({
namePrefix: "ci-", // Filter by name prefixtags: { env: "staging" }, // Filter by tagssortBy: "createdAt", // "createdAt" (default), "name", or "statusUpdatedAt"sortOrder: "desc", // "asc" or "desc" (default)limit: 50,
});
// Per-item async iteration (auto-paginates)forawait (const sandbox of result) {
console.log(sandbox.name);
}
// Per-page iterationforawait (const page of result.pages()) {
console.log(page.sandboxes.length);
}
// Collect everythingconst all = await result.toArray();
// Or use the cursor directlyconst next = result.pagination.next;
sandbox.listSessions and sandbox.listSnapshots
// List all VM sessions for this sandboxconst sessions = await sandbox.listSessions();
forawait (const session of sessions) {
console.log(session.sessionId, session.status);
}
// List snapshots belonging to this sandboxconst snapshots = await sandbox.listSnapshots();
forawait (const snapshot of snapshots) {
console.log(snapshot.snapshotId, snapshot.status);
}
A Session is a single running VM instance inside a sandbox. You typically
do not interact with sessions directly — the SDK creates and resumes them for
you — but you can inspect the current one.
Snapshots save the entire sandbox filesystem to be reused later, for any
number of sandboxes.
Snapshots are stored in the sandbox's region; all regions where a snapshot is
available are listed in snapshot.regions. Creating a sandbox from a snapshot
in a region where the snapshot is not available fails with a
snapshot_region_mismatch error.
Create a Snapshot
const sandbox = awaitSandbox.create();
await sandbox.runCommand("npm", ["install"]);
// Create snapshot (stops the sandbox)const snapshot = await sandbox.snapshot({
expiration: ms("14d"), // Default: 30 days, use 0 for no expiration
});
console.log("Snapshot ID:", snapshot.snapshotId);
Default Snapshot Expiration and Retention
Configure default expiration and retention policy per sandbox:
awaitSandbox.create({
name: "my-app",
snapshotExpiration: ms("7d"), // Default TTL for any snapshot of this sandboxkeepLastSnapshots: {
count: 1, // Keep only the most recent snapshot (1-10)expiration: ms("30d"), // Override expiration for kept snapshotsdeleteEvicted: true, // Delete evicted snapshots immediately (default)
},
});
keepLastSnapshots: { count: 1 } is the recommended setting when you only
care about the latest snapshot — it lets the SDK keep snapshot storage costs flat.
List, Get, and Delete
// List all snapshots in the project (auto-paginates)const snapshots = awaitSnapshot.list();
forawait (const snapshot of snapshots) {
console.log(snapshot.snapshotId, snapshot.status);
}
// Get a specific snapshotconst snapshot = awaitSnapshot.get({ snapshotId: "snap_abc123" });
// Delete a snapshotawait snapshot.delete();
Snapshot Tree
Snapshots form a tree: any sandbox created from another snapshot inherits a
parent → child relationship. Walk that tree to see ancestors or descendants of
a given snapshot.
4 vCPUs on Hobby, 8 vCPUs on Pro, 32 vCPUs on Enterprise (2048 MB RAM per vCPU)
Max ports
15 exposed ports
Max tags
5 key-value tags per sandbox
Max timeout
24 hours (Pro/Enterprise), 45 minutes (Hobby)
Default timeout
5 minutes
Base system
Ubuntu 26.04
User context
ubuntu user
Writable path
/vercel/sandbox
Regions
One primary region per sandbox, plus optional failover regions. Snapshots restore only in regions where they are available. See the Vercel docs for the region list.
System Packages
The default image includes Node.js 24, Bun, Python 3.14, pnpm, uv, Git, GitHub
CLI, coding agents, and common development and debugging utilities.
Maintain a single "base" sandbox with dependencies installed, and spawn fresh
children from it with Sandbox.fork. Each fork inherits the base's config
and is seeded from its current snapshot — no need to store snapshot IDs in
your code. New base snapshots are picked up automatically on the next fork.