Sandbox SDK Reference
The Vercel Sandbox Software Development Kit (SDK) lets you create ephemeral Linux microVMs on demand. Use it to evaluate user-generated code, run AI agent output safely, test services without touching production resources, or run reproducible integration tests that need a full Linux environment with sudo access.
Install the SDK:
pnpm i @vercel/sandboxAfter installation:
- Link your project and pull environment variables with
vercel linkandvercel env pullso the SDK can read a Vercel OpenID Connect (OIDC) token. - Choose a runtime:
node24,node22, orpython3.13.
| Class | What it does | Example |
|---|---|---|
Sandbox | Creates and manages isolated microVM environments | const sandbox = await Sandbox.create() |
Command | Handles running commands inside the sandbox | const cmd = await sandbox.runCommand() |
CommandFinished | Contains the result after a command completes | Access cmd.exitCode and cmd.stdout() |
// 1. Create a sandbox
const sandbox = await Sandbox.create({ runtime: 'node24' });
// 2. Run a command - it waits for completion and returns the result
const result = await sandbox.runCommand('node', ['--version']);
// 3. Check the result
console.log(result.exitCode); // 0
console.log(await result.stdout()); // v22.x.xThe Sandbox class gives you full control over isolated Linux microVMs. Use it to create new sandboxes, inspect active ones, stream command output, and shut everything down once your workflow is complete.
Use sandboxId to identify the current microVM so you can reconnect to it later with Sandbox.get() or trace command history. Store this ID whenever your workflow spans multiple processes or retries so you can resume log streaming after a restart.
Returns: string.
console.log(sandbox.sandboxId);The status accessor reports the lifecycle state of the sandbox so you can decide when to queue new work or perform cleanup. Poll this value when you need to wait for startup or confirm shutdown, and treat failed as a signal to create a new sandbox.
Returns: "pending" | "running" | "stopping" | "stopped" | "failed".
console.log(sandbox.status);timeout shows how many milliseconds remain before the sandbox stops automatically. Compare the remaining time against upcoming commands and call sandbox.extendTimeout() if the window is too short.
Returns: number.
console.log(sandbox.timeout);The createdAt accessor returns the date and time when the sandbox was created. Use this to track sandbox age or calculate how long a sandbox has been running.
Returns: Date.
console.log(sandbox.createdAt);Use Sandbox.list() to enumerate sandboxes for a project, optionally filtering by time range or page size. Combine since and until with the pagination cursor and cache the last pagination.next value so you can resume after restarts without missing entries.
Returns: Promise<Parsed<{ sandboxes: SandboxSummary[]; pagination: Pagination; }>>.
| Parameter | Type | Required | Details |
|---|---|---|---|
projectId | string | Yes | Project whose sandboxes you want to list. |
limit | number | No | Maximum number of sandboxes to return. |
since | number | Date | No | List sandboxes created after this time. |
until | number | Date | No | List sandboxes created before this time. |
signal | AbortSignal | No | Cancel the request if necessary. |
const { sandboxes } = await Sandbox.list({ projectId });Sandbox.create() launches a new microVM with your chosen runtime, source, and resource settings. Defaults to an empty workspace when no source is provided. Pass source.depth when cloning large repositories to shorten setup time.
Returns: Promise<Sandbox>.
| Parameter | Type | Required | Details / Values |
|---|---|---|---|
source | git | No | Clone a Git repository. url: string username: string password: string depth?: number revision?: string |
source | tarball | No | Mount a tarball. url: string |
resources.vcpus | number | No | Override CPU count (defaults to plan baseline). |
runtime | string | No | Runtime image such as "node24", "node22", or "python3.13". |
ports | number[] | No | Ports to expose for sandbox.domain(). |
timeout | number | No | Initial timeout in milliseconds. |
signal | AbortSignal | No | Cancel sandbox creation if needed. |
const sandbox = await Sandbox.create({ runtime: 'node24' });Sandbox.get() rehydrates an active sandbox by ID so you can resume work or inspect logs. It throws if the sandbox no longer exists, so cache sandboxId only while the job is active and clear it once the sandbox stops.
Returns: Promise<Sandbox>.
| Parameter | Type | Required | Details |
|---|---|---|---|
sandboxId | string | Yes | Identifier of the sandbox to retrieve. |
signal | AbortSignal | No | Cancel the request if necessary. |
const sandbox = await Sandbox.get({ sandboxId });Call sandbox.getCommand() to retrieve a previously executed command by its ID, which is especially helpful after detached executions when you want to inspect logs later.
Returns: Promise<Command>.
| Parameter | Type | Required | Details |
|---|---|---|---|
cmdId | string | Yes | Identifier of the command to fetch. |
opts.signal | AbortSignal | No | Cancel the lookup if it takes too long. |
const command = await sandbox.getCommand(cmdId);sandbox.runCommand() executes commands inside the microVM, either blocking until completion or returning immediately in detached mode. Use detached: true for long-running servers, stream output to local log handlers, and call command.wait() later for results.
Returns: Promise<CommandFinished> when detached is falsy; Promise<Command> when detached is true.
| Parameter | Type | Required | Details |
|---|---|---|---|
command | string | Yes | Command to execute (string overload). |
args | string[] | No | Arguments for the string overload. |
opts.signal | AbortSignal | No | Cancel the command (string overload). |
params.cmd | string | Yes | Command to execute when using the object overload. |
params.args | string[] | No | Arguments for the object overload. |
params.cwd | string | No | Working directory for execution. |
params.env | Record<string, string> | No | Additional environment variables. |
params.sudo | boolean | No | Run the command with sudo. |
params.detached | boolean | No | Return immediately with a live Command object. |
params.stdout | Writable | No | Stream standard output to a writable. |
params.stderr | Writable | No | Stream standard error to a writable. |
params.signal | AbortSignal | No | Cancel the command when using the object overload. |
const result = await sandbox.runCommand('node', ['--version']);sandbox.mkDir() creates directories in the sandbox filesystem before you write files or clone repositories. Paths are relative to /vercel/sandbox unless you provide an absolute path, so call this before writeFiles() when you need nested folders.
await sandbox.mkDir('tmp/assets');| Parameter | Type | Required | Details |
|---|---|---|---|
path | string | Yes | Directory to create. |
opts.signal | AbortSignal | No | Cancel the operation. |
Returns: Promise<void>.
Use sandbox.readFile() to pull file contents from the sandbox. The promise resolves to null when the file does not exist, so convert the returned stream to text or JSON before use.
const stream = await sandbox.readFile({ path: 'package.json' });| Parameter | Type | Required | Details |
|---|---|---|---|
file.path | string | Yes | Path to the file inside the sandbox. |
file.cwd | string | No | Base directory for resolving file.path. |
opts.signal | AbortSignal | No | Cancel the read operation. |
Returns: Promise<null | ReadableStream>.
sandbox.writeFiles() uploads one or more files into the sandbox filesystem. Paths default to /vercel/sandbox; use absolute paths for custom locations and bundle related files into a single call to reduce round trips.
await sandbox.writeFiles([{ path: 'hello.txt', content: Buffer.from('hi') }]);| Parameter | Type | Required | Details |
|---|---|---|---|
files | { path: string; content: Buffer; }[] | Yes | File descriptors to write. |
opts.signal | AbortSignal | No | Cancel the write operation. |
Returns: Promise<void>.
sandbox.domain() resolves a publicly accessible URL for a port you exposed during creation. It throws if the port is not registered to a route, so include the port in the ports array when creating the sandbox and cache the returned URL so you can share it quickly with collaborators.
const previewUrl = sandbox.domain(3000);| Parameter | Type | Required | Details |
|---|---|---|---|
p | number | Yes | Port number declared in ports. |
Returns: string.
Call sandbox.stop() to terminate the microVM and free resources immediately. It's safe to call multiple times; subsequent calls resolve once the sandbox is already stopped, so invoke it as soon as you collect artifacts to control costs.
await sandbox.stop();| Parameter | Type | Required | Details |
|---|---|---|---|
opts.signal | AbortSignal | No | Cancel the stop operation. |
Returns: Promise<void>.
Use sandbox.extendTimeout() to extend the sandbox lifetime by the specified duration. This lets you keep the sandbox running up to the maximum execution timeout for your plan, so check sandbox.timeout first and extend only when necessary to avoid premature shutdown.
await sandbox.extendTimeout(60000); // Extend by 60 seconds| Parameter | Type | Required | Details |
|---|---|---|---|
duration | number | Yes | Duration in milliseconds to extend the timeout by. |
opts.signal | AbortSignal | No | Cancel the operation. |
Returns: Promise<void>.
Command instances represent processes that run inside a sandbox. Detached executions created through sandbox.runCommand({ detached: true, ... }) return a Command immediately so that you can stream logs or stop the process later. Blocking executions that do not set detached still expose these methods through the CommandFinished object they resolve to.
The exitCode property holds the process exit status once the command finishes. For detached commands, this value starts as null and gets populated after you await command.wait(), so check for null to determine if the command is still running.
if (command.exitCode !== null) {
console.log(`Command exited with code: ${command.exitCode}`);
}Returns: number | null.
Use cmdId to identify the specific command execution so you can look it up later with sandbox.getCommand(). Store this value whenever you launch detached commands so you can replay output in dashboards or correlate logs across systems.
console.log(command.cmdId);Returns: string.
The cwd accessor shows the working directory where the command is executing. Compare this value against expected paths when debugging file-related issues or verifying that relative paths resolve correctly.
console.log(command.cwd);Returns: string.
startedAt returns the Unix timestamp (in milliseconds) when the command started executing. Subtract this from the current time to monitor execution duration or set timeout thresholds for long-running processes.
const duration = Date.now() - command.startedAt;
console.log(`Command has been running for ${duration}ms`);Returns: number.
Call logs() to stream structured log entries in real time so you can watch command output as it happens. Each entry includes the stream type (stdout or stderr) and the data chunk, so you can route logs to different destinations or stop iteration when you detect a readiness signal.
for await (const log of command.logs()) {
if (log.stream === 'stdout') {
process.stdout.write(log.data);
} else {
process.stderr.write(log.data);
}
}| Parameter | Type | Required | Details |
|---|---|---|---|
opts.signal | AbortSignal | No | Cancel log streaming if needed. |
Returns: AsyncGenerator<{ stream: "stdout" | "stderr"; data: string; }, void, void>.
Note: May throw StreamError if the sandbox stops while streaming logs.
Use wait() to block until a detached command finishes and get the resulting CommandFinished object with the populated exit code. This method is essential for detached commands where you need to know when execution completes. For non-detached commands, sandbox.runCommand() already waits automatically.
const detachedCmd = await sandbox.runCommand({
cmd: 'sleep',
args: ['5'],
detached: true,
});
const result = await detachedCmd.wait();
if (result.exitCode !== 0) {
console.error('Something went wrong...');
}| Parameter | Type | Required | Details |
|---|---|---|---|
params.signal | AbortSignal | No | Cancel waiting if you need to abort early. |
Returns: Promise<CommandFinished>.
Use output() to retrieve stdout, stderr, or both as a single string. Choose "both" when you want combined output for logging, or specify "stdout" or "stderr" when you need to process them separately after the command finishes.
const combined = await command.output('both');
const stdoutOnly = await command.output('stdout');| Parameter | Type | Required | Details |
|---|---|---|---|
stream | "stdout" | "stderr" | "both" | Yes | The output stream to read. |
opts.signal | AbortSignal | No | Cancel output streaming. |
Returns: Promise<string>.
Note: This may throw string conversion errors if the command output contains invalid Unicode.
stdout() collects the entire standard output stream as a string, which is handy when commands print JSON or other structured data that you need to parse after completion.
const output = await command.stdout();
const data = JSON.parse(output);| Parameter | Type | Required | Details |
|---|---|---|---|
opts.signal | AbortSignal | No | Cancel the read while the command runs. |
Returns: Promise<string>.
Note: This may throw string conversion errors if the command output contains invalid Unicode.
stderr() gathers all error output produced by the command. Combine this with exitCode to build user-friendly error messages or forward failure logs to your monitoring system.
const errors = await command.stderr();
if (errors) {
console.error('Command errors:', errors);
}| Parameter | Type | Required | Details |
|---|---|---|---|
opts.signal | AbortSignal | No | Cancel the read while collecting error output. |
Returns: Promise<string>.
Note: This may throw string conversion errors if the command output contains invalid Unicode.
Call kill() to terminate a running command using the specified signal. This lets you stop long-running processes without destroying the entire sandbox. Send SIGTERM by default for graceful shutdown, or use SIGKILL for immediate termination.
await command.kill('SIGKILL');| Parameter | Type | Required | Details |
|---|---|---|---|
signal | Signal | No | The signal to send to the process. Defaults to SIGTERM. |
opts.abortSignal | AbortSignal | No | Cancel the kill operation. |
Returns: Promise<void>.
CommandFinished is the result you receive after a sandbox command exits. It extends the Command class, so you keep access to streaming helpers such as logs() or stdout(), but you also get the final exit metadata immediately. You usually receive this object from sandbox.runCommand() or by awaiting command.wait() on a detached process.
The exitCode property reports the numeric status returned by the command. A value of 0 indicates success; any other value means the process exited with an error, so branch on it before you parse output.
if (result.exitCode === 0) {
console.log('Command succeeded');
}Returns: number.
Use cmdId to identify the specific command execution so you can reference it in logs or retrieve it later with sandbox.getCommand(). Store this ID whenever you need to trace command history or correlate output across retries.
console.log(result.cmdId);Returns: string.
The cwd accessor shows the working directory where the command executed. Compare this value against expected paths when debugging file-related failures or relative path issues.
console.log(result.cwd);Returns: string.
startedAt returns the Unix timestamp (in milliseconds) when the command started executing. Subtract this from the current time or from another timestamp to measure execution duration or schedule follow-up tasks.
const duration = Date.now() - result.startedAt;
console.log(`Command took ${duration}ms`);Returns: number.
CommandFinished inherits all methods from Command including logs(), output(), stdout(), stderr(), and kill(). See the Command class section for details on these methods.
- Clone and run a private Git repository to validate builds before merging pull requests.
- Install system packages while keeping sudo-enabled commands isolated.
- Extend sandbox timeouts to support longer-running workloads like training or large dependency installs.
- Browse more scenarios in the Sandbox examples catalog.
The SDK prioritizes Vercel OIDC tokens that the CLI stores in .env.local. Link your project and pull environment variables with vercel link and vercel env pull.
Sandbox credentials expire. Rotate downloaded tokens at least every 12 hours during development.
For more background, read the authentication guide or the Vercel Sandbox authentication section.
- Operating system: Amazon Linux 2023 with common build tools such as
git,tar,openssl, anddnf. - Available runtimes:
node24,node22, andpython3.13images with their respective package managers. - Resources: Choose the number of virtual CPUs (
vcpus) per sandbox. Pricing and plan limits appear in the Sandbox pricing table. - Timeouts: The default timeout is 5 minutes. You can extend it programmatically up to 45 minutes on the Hobby plan and up to 5 hours on Pro and Enterprise plans.
- Sudo:
sudocommands run asvercel-sandboxwith the root home directory set to/root.
The filesystem is ephemeral. You must export artifacts to durable storage if you need to keep them after the sandbox stops.
Was this helpful?