# @vercel/sandbox

> Software Development Kit for Vercel Sandbox

Latest version **3.3.0** (published 2026-09-11) · Apache-2.0 license · 0 weekly downloads

## Install

```sh
npm install @vercel/sandbox
pnpm add @vercel/sandbox
yarn add @vercel/sandbox
bun add @vercel/sandbox
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 3.3.0 |
| Published | 2026-09-11 |
| First published | 2025-05-20 |
| Weekly downloads | 0 |
| License | Apache-2.0 |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 12 |
| Unpacked size | 1.5 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 200 |
| Maintainers | matheuss, matt.straka, vercel-release-bot, zeit-bot |
| Keywords | vercel, sandbox, sdk |

## Links

- npm: https://www.npmjs.com/package/@vercel/sandbox
- Repository: https://github.com/vercel/sandbox
- Homepage: https://vercel.com/docs/vercel-sandbox/sdk-reference
- Issues: https://github.com/vercel/sandbox/issues
- npm.io page: https://npm.io/package/@vercel/sandbox

## Dependencies (12)

- [ms](https://npm.io/package/ms.md) 2.1.3
- [zod](https://npm.io/package/zod.md) ^4.1.1
- [jose](https://npm.io/package/jose.md) 6.2.3
- [undici](https://npm.io/package/undici.md) ^7.27.1
- [jsonlines](https://npm.io/package/jsonlines.md) 0.1.1
- [lru-cache](https://npm.io/package/lru-cache.md) ^10.4.3
- [picocolors](https://npm.io/package/picocolors.md) ^1.1.1
- [tar-stream](https://npm.io/package/tar-stream.md) 3.1.7
- [async-retry](https://npm.io/package/async-retry.md) 1.3.3
- [@vercel/oidc](https://npm.io/package/@vercel/oidc.md) 3.2.0
- [xdg-app-paths](https://npm.io/package/xdg-app-paths.md) 5.1.0
- [@workflow/serde](https://npm.io/package/@workflow/serde.md) 4.1.0-beta.2

## Alternatives

- [jsforce](https://npm.io/package/jsforce.md) — 851.2K weekly downloads
- [react-native-qrcode-svg](https://npm.io/package/react-native-qrcode-svg.md) — 693.5K weekly downloads
- [@salesforce/plugin-data](https://npm.io/package/@salesforce/plugin-data.md) — 394.9K weekly downloads
- [@backstage/plugin-search-common](https://npm.io/package/@backstage/plugin-search-common.md) — 308.5K weekly downloads
- [@chain-registry/types](https://npm.io/package/@chain-registry/types.md) — 38.4K weekly downloads

## Recent versions

- 3.3.0 (latest) — 2026-09-11
- 3.4.0-beta.0 (beta) — 2026-09-10
- 3.4.0-beta — 2026-09-08
- 3.2.2 — 2026-09-08
- 3.3.0-beta.0 — 2026-09-07
- 3.2.1 — 2026-08-28
- 3.2.0 — 2026-08-27
- 3.1.0-beta.1 — 2026-08-21
- 3.1.0 — 2026-08-21
- 3.0.1 — 2026-08-20
- 3.1.0-beta.0 — 2026-08-10
- 3.0.0 — 2026-08-07
- 2.10.0-beta.0 — 2026-08-04
- 2.9.2 — 2026-07-30
- 2.9.1 — 2026-07-30
- … 98 more at https://npm.io/package/@vercel/sandbox/versions

## README

# Vercel Sandbox

Vercel Sandbox allows you to run arbitrary code in isolated, ephemeral Linux
VMs. View the documentation [here](https://vercel.com/docs/vercel-sandbox).

## Packages

- [`@vercel/sandbox`](https://www.npmjs.com/package/@vercel/sandbox) (this package) - The SDK for programmatic access to Vercel Sandbox. [Source](https://github.com/vercel/sandbox/tree/main/packages/vercel-sandbox) | [Documentation](https://vercel.com/docs/vercel-sandbox/sdk-reference)
- [`sandbox`](https://www.npmjs.com/package/sandbox) - The CLI for interacting with Vercel Sandbox from the command line. [Source](https://github.com/vercel/sandbox/tree/main/packages/sandbox) | [Documentation](https://vercel.com/docs/vercel-sandbox/cli-reference)

## What is a sandbox?

A sandbox is an isolated Linux system for your experimentation and use.
Internally, it is a Firecracker MicroVM that is powered by [the same
infrastructure][hive] that powers 2M+ builds a day at Vercel.

## Getting started

To get started using Ubuntu with Node.js 24, create a new project:

```sh
mkdir my-sandbox-app && cd my-sandbox-app
npm init -y
vercel link
```

Pull your authentication token:

```sh
vercel env pull
```

Install the Sandbox SDK:

```sh
pnpm i @vercel/sandbox
```

Install the Sandbox Skill:

```sh
npx skills add vercel/sandbox
```

Create a `index.mts` file:

```ts
import { Sandbox } from "@vercel/sandbox";
import { setTimeout } from "timers/promises";
import { spawn } from "child_process";

async function main() {
  const sandbox = await Sandbox.create({
    source: {
      url: "https://github.com/vercel/sandbox-example-next.git",
      type: "git",
    },
    resources: { vcpus: 4 },
    ports: [3000],
    name: "vercel-sandbox-example",
  });
  console.log(`Sandbox ${sandbox.name} created`);

  console.log(`Installing dependencies...`);
  const install = await sandbox.runCommand({
    cmd: "npm",
    args: ["install", "--loglevel", "info"],
    cwd: "sandbox-example-next",
    stderr: process.stderr,
    stdout: process.stdout,
  });

  if (install.exitCode !== 0) {
    console.log("installing packages failed");
    process.exit(1);
  }

  console.log(`Starting the development server...`);
  await sandbox.runCommand({
    cmd: "npm",
    args: ["run", "dev"],
    cwd: "sandbox-example-next",
    stderr: process.stderr,
    stdout: process.stdout,
    detached: true,
  });

  await setTimeout(500);
  spawn("open", [sandbox.domain(3000)]);
}

main().catch(console.error);
```

Run it:

```sh
node --experimental-strip-types --env-file .env.local index.mts
```

This will:

- Start a sandbox, seeding it with a git repository.
- Install dependencies.
- Run a `next dev` server
- Open it in your browser

All while streaming logs to your local terminal.

Sandboxes are persistent by default. To resume a sandbox with its previous state:

Create a `resume.mts` file:

```ts
import { Sandbox } from "@vercel/sandbox";
import { setTimeout } from "timers/promises";
import { spawn } from "child_process";

async function main() {
  const sandbox = await Sandbox.get({
    name: "vercel-sandbox-example",
  });
  console.log(`Sandbox ${sandbox.name} resumed`);

  console.log(`Starting the development server...`);
  await sandbox.runCommand({
    cmd: "npm",
    args: ["run", "dev"],
    cwd: "sandbox-example-next",
    stderr: process.stderr,
    stdout: process.stdout,
    detached: true,
  });

  await setTimeout(500);
  spawn("open", [sandbox.domain(3000)]);
}

main().catch(console.error);
```

Run it:

```sh
node --experimental-strip-types --env-file .env.local resume.mts
```

## Authentication

### Vercel OIDC token

The SDK uses Vercel OIDC tokens to authenticate whenever available. This is the
most straightforward and recommended way to authenticate.

When developing locally, you can download a development token to `.env.local`
using `vercel env pull`. After 12 hours the development token expires, meaning
you will have to call `vercel env pull` again.

In production, Vercel manages token expiration for you.

### Access token

If you want to use the SDK from an environment where `VERCEL_OIDC_TOKEN` is
unavailable, you can also authenticate using an access token:

- Go to your team settings, and copy the team ID.
- Go to a project's settings, and copy the project ID.
- Go to your Vercel account settings and [create a token][create-token]. Make
  sure it is scoped to the team ID from the previous step.

Set your team ID, project ID, and token to the environment variables
`VERCEL_TEAM_ID`, `VERCEL_PROJECT_ID`, and `VERCEL_TOKEN`. Then pass these to
the `create` method:

```ts
const sandbox = await Sandbox.create({
  teamId: process.env.VERCEL_TEAM_ID!,
  projectId: process.env.VERCEL_PROJECT_ID!,
  token: process.env.VERCEL_TOKEN!,
  source: {
    url: "https://github.com/vercel/sandbox-example-next.git",
    type: "git",
  },
  resources: { vcpus: 4 },
  // Defaults to 5 minutes. The maximum is 24 hours for Pro/Enterprise, and 45 minutes for Hobby.
  timeout: ms("5m"),
  ports: [3000],
});
```

## Workflow DevKit integration

`Sandbox` and `CommandFinished` support serialization with the
[Workflow DevKit](https://vercel.com/docs/workflow). When a sandbox instance
crosses a step boundary the SDK serializes sandbox metadata and routes, then
rehydrates synchronously from that snapshot. Deserialized instances lazily
recreate an API client using OIDC or environment credentials when needed.

## Limitations

- Max resources: 4 vCPUs on Hobby, 8 vCPUs on Pro, 32 vCPUs on Enterprise. You will get 2048 MB of memory per vCPU.
- Sandboxes have a maximum duration of 24 hours for Pro/Enterprise and 45 minutes for Hobby,
  with a default of 5 minutes. This can be configured using the `timeout` option of `Sandbox.create()`.

## Default image

Sandboxes use
[`vercel/sandbox/universal:latest`](https://github.com/vercel/sandbox/tree/main/images/universal)
by default. This Ubuntu-based image includes Node.js 24, Bun, Python 3.14,
coding agents, and common development and debugging utilities. It runs as the
`ubuntu` user with passwordless sudo.

## Vercel Managed Images

Vercel provides several public images optimized to use in Sandbox. The
Dockerfiles for Vercel Managed Images published under `vercel/sandbox/*` live
in the [`images/`](https://github.com/vercel/sandbox/tree/main/images)
directory:

- [`vercel/sandbox/universal:latest`](https://github.com/vercel/sandbox/tree/main/images/universal): Default image with Node.js, Python, coding agents, and utilities.
- [`vercel/sandbox/node:22|24|26`](https://github.com/vercel/sandbox/tree/main/images/node): Node.js with pnpm.
- [`vercel/sandbox/python:3.14`](https://github.com/vercel/sandbox/tree/main/images/python): Python with pip, venv, and uv.
- [`vercel/sandbox/ubuntu:latest`](https://github.com/vercel/sandbox/tree/main/images/ubuntu): Minimal Ubuntu base.
- [`vercel/sandbox/arch:latest`](https://github.com/vercel/sandbox/tree/main/images/arch): Arch Linux with yay/AUR support.

See the [images README](https://github.com/vercel/sandbox/tree/main/images#readme)
for build instructions.

### Custom images

A sandbox can boot from any OCI image by pushing it to
[Vercel Container Registry (VCR)][vcr-docs] and passing `image` to
`Sandbox.create()`.

Build and push a `linux/amd64` image to VCR:

```sh
vercel vcr login docker
vercel vcr build docker . my-repository:latest --push
```

The CLI uses the linked project, defaults to `linux/amd64`, and constructs the
full VCR reference automatically.

VCR implements the Docker Registry API, so any OCI compatible tooling can also
be used, such as `buildah` or `podman`.

Then start a sandbox from it:

```typescript
import { Sandbox } from "@vercel/sandbox";

const sandbox = await Sandbox.create({
  image: "my-repository:latest",
});
```

The `image` option accepts a repository in the sandbox's project, with an
optional tag or digest. A bare repository name resolves to the `latest` tag.
You can also pass a fully-qualified VCR URL:

```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
});
```

See the [images documentation][images-docs] for more details.

## Sudo access

The default image allows users to run commands as root. This can be used to
install packages and system tools:

```typescript
import { Sandbox } from "@vercel/sandbox";

const sandbox = await Sandbox.create();
await sandbox.runCommand({
  cmd: "apt-get",
  args: ["update"],
  sudo: true,
});
await sandbox.runCommand({
  cmd: "apt-get",
  args: ["install", "-y", "golang-go"],
  sudo: true,
});
```

Sandbox runs sudo in the following configuration:

- `HOME` is set to `/root` – Executed commands will source root's configuration
  files (e.g. `.gitconfig`, `.bashrc`, etc).
- Environment variables are not reset before executing the command.
- `PATH` is left unchanged – sudo won't change the value of PATH, so local or
  project-specific binaries will still be found.

## Multi-user

Sandboxes support creating isolated Linux users with their own home directories,
file permissions, and process ownership. This is useful for multi-agent workflows
where each agent needs its own workspace, or for simulating multi-user
environments.

> **Note:** The sandbox image must have `/bin/bash` installed. It is the login
> shell for created users and is used to wrap commands that run as a user. The
> Vercel managed images include it.

### Creating users

```typescript
import { Sandbox } from "@vercel/sandbox";

const sandbox = await Sandbox.create();

// Creates /home/alice with isolated permissions
const alice = await sandbox.createUser("alice");

alice.username; // "alice"
alice.homeDir; // "/home/alice"
```

`createUser` sets up:

- A Linux user with `/bin/bash` as the default shell
- A home directory at `/home/<username>` group-owned by the sandbox's default user group with `770` permissions

### Running commands as a user

All commands run as the user by default, with the working directory set to their
home:

```typescript
const alice = await sandbox.createUser("alice");

const whoami = await alice.runCommand("whoami");
await whoami.stdout(); // "alice\n"

const pwd = await alice.runCommand("pwd");
await pwd.stdout(); // "/home/alice\n"
```

You can pass environment variables, override the working directory, or use the
full `RunCommandParams` interface:

```typescript
// Environment variables
await alice.runCommand({
  cmd: "node",
  args: ["-e", "console.log(process.env.API_KEY)"],
  env: { API_KEY: "secret" },
});

// Custom working directory
await alice.runCommand({ cmd: "ls", cwd: "/tmp" });

// Detached mode for long-running processes
const server = await alice.runCommand({
  cmd: "node",
  args: ["server.js"],
  detached: true,
});
```

To escalate to root, pass `sudo: true`:

```typescript
await alice.runCommand({
  cmd: "apt-get",
  args: ["update"],
  sudo: true,
});
await alice.runCommand({
  cmd: "apt-get",
  args: ["install", "-y", "git"],
  sudo: true,
});
```

### File operations

`writeFiles`, `readFile`, `readFileToBuffer`, and `mkDir` all resolve relative
paths against the user's home directory. Written files are owned by the user:

```typescript
const alice = await sandbox.createUser("alice");

// Writes to /home/alice/app.js, owned by alice:alice
await alice.writeFiles([
  { path: "app.js", content: Buffer.from('console.log("hi")') },
]);

// Read it back
const buf = await alice.readFileToBuffer({ path: "app.js" });
buf?.toString(); // 'console.log("hi")'

// Stream reads
const stream = await alice.readFile({ path: "app.js" });

// Create directories owned by the user
await alice.mkDir("projects/my-app");

// Absolute paths also work
await alice.writeFiles([
  { path: "/tmp/output.txt", content: Buffer.from("data") },
]);
```

### File isolation

Users cannot access each other's home directories:

```typescript
const alice = await sandbox.createUser("alice");
const bob = await sandbox.createUser("bob");

await alice.writeFiles([
  { path: "secret.txt", content: Buffer.from("alice only") },
]);

// Bob cannot read, list, or write to alice's home
const cat = await bob.runCommand({
  cmd: "cat",
  args: ["/home/alice/secret.txt"],
});
cat.exitCode; // non-zero — Permission denied
```

**The SDK can read all users' files** because home directories are group-owned
by the sandbox's default user group. Both `SandboxUser` methods and direct
`sandbox` methods work:

```typescript
// Via SandboxUser (relative paths resolve to home dir)
const buf = await alice.readFileToBuffer({ path: "secret.txt" });
buf?.toString(); // "alice only"

// Via sandbox directly (absolute path required)
const buf2 = await sandbox.readFileToBuffer({ path: "/home/alice/secret.txt" });
buf2?.toString(); // "alice only"
```

### Groups and shared directories

Create groups to let users collaborate through a shared directory:

```typescript
const devs = await sandbox.createGroup("devs");
devs.sharedDir; // "/shared/devs"

await sandbox.addUserToGroup("alice", "devs");
await sandbox.addUserToGroup("bob", "devs");

// Alice writes to the shared directory
await alice.runCommand({
  cmd: "bash",
  args: ["-c", 'echo "spec v2" > /shared/devs/spec.txt'],
});

// Bob can read it — files inherit group ownership via setgid
const spec = await bob.runCommand({
  cmd: "cat",
  args: ["/shared/devs/spec.txt"],
});
await spec.stdout(); // "spec v2\n"

// Non-members are blocked
const charlie = await sandbox.createUser("charlie");
const ls = await charlie.runCommand({ cmd: "ls", args: ["/shared/devs"] });
ls.exitCode; // non-zero — Permission denied
```

Shared directories use setgid (`2770`), so files created inside them
automatically inherit the group. All group members get read/write access.

Convenience methods are available on `SandboxUser`:

```typescript
await alice.addToGroup("devs");
await alice.removeFromGroup("devs");
```

### Using `asUser` for existing users

If a user already exists (e.g., from a snapshot or manual creation), use
`asUser` to get a handle without re-creating:

```typescript
const existing = sandbox.asUser("bob");
await existing.runCommand("whoami"); // "bob"
```

### Username validation

Usernames and group names must match `/^[a-z_][a-z0-9_-]*$/` and be at most 32
characters. Invalid names throw an error immediately:

```typescript
sandbox.asUser("Alice"); // throws — uppercase
sandbox.asUser("user name"); // throws — space
sandbox.asUser("$(whoami)"); // throws — special characters
sandbox.asUser("a".repeat(33)); // throws — too long
```

### Multi-agent example

```typescript
const sandbox = await Sandbox.create();

// Each agent gets its own isolated workspace
const researcher = await sandbox.createUser("researcher");
const coder = await sandbox.createUser("coder");
const reviewer = await sandbox.createUser("reviewer");

// Shared workspace for collaboration
await sandbox.createGroup("project");
await sandbox.addUserToGroup("researcher", "project");
await sandbox.addUserToGroup("coder", "project");
await sandbox.addUserToGroup("reviewer", "project");

// Researcher writes findings to shared dir
await researcher.runCommand({
  cmd: "bash",
  args: ["-c", 'echo "API spec v2" > /shared/project/spec.txt'],
});

// Coder reads spec, writes code in their own home
const spec = await coder.runCommand({
  cmd: "cat",
  args: ["/shared/project/spec.txt"],
});
await coder.writeFiles([
  { path: "app.js", content: Buffer.from(`// ${await spec.stdout()}`) },
]);

// Reviewer can read the shared spec but not coder's private files
const blocked = await reviewer.runCommand({
  cmd: "cat",
  args: ["/home/coder/app.js"],
});
blocked.exitCode; // non-zero — isolation enforced
```

[create-token]: https://vercel.com/account/settings/tokens
[hive]: https://vercel.com/blog/a-deep-dive-into-hive-vercels-builds-infrastructure
[vcr-docs]: https://vercel.com/docs/container-registry
[images-docs]: https://vercel.com/docs/sandbox/concepts/images

## Authors

This library is created by [Vercel](https://vercel.com) team members, with contributions from the [Open Source Community](https://github.com/vercel/sandbox/graphs/contributors) welcome and highly appreciated.

---
_Source: https://npm.io/package/@vercel/sandbox · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
