Working with Sandbox
Use Edgenivo Sandbox to run code, stream command output, manage files, capture snapshots, and stop sandboxes from your application or from the terminal.
Create a sandbox, write a file into it, run the file, and inspect the command output.
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
timeout: 60_000,
});
try {
await sandbox.writeFiles([
{
path: 'hello.js',
content: Buffer.from("console.log('Hello from Edgenivo Sandbox!')\n"),
},
]);
const result = await sandbox.runCommand('node', ['hello.js']);
if (result.exitCode !== 0) {
throw new Error(await result.stderr());
}
console.log(await result.stdout());
} finally {
await sandbox.stop();
}
You can use any of the Edgenivo Managed Image, or start from your own or a shared custom image hosted on Edgenivo Container Registry. See Images for how to use it:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
image: 'my-repository:latest',
});
Pass a git source to create a sandbox from a repository. For a private repository, add credentials with username and password. GitHub accepts x-access-token as the username and a token as the password:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
source: {
type: 'git',
url: 'https://github.com/your-org/private-repo.git',
username: 'x-access-token',
password: process.env.GIT_ACCESS_TOKEN!,
depth: 1,
},
ports: [3000],
});
const install = await sandbox.runCommand('npm', ['install']);
if (install.exitCode !== 0) {
throw new Error(await install.stderr());
}
await sandbox.runCommand({ cmd: 'npm', args: ['run', 'dev'], detached: true });
console.log(sandbox.domain(3000));
The Sandbox CLI doesn't accept a Git source. From the CLI, clone the repository inside the sandbox with sandbox exec and your own credentials.
Choose the GitHub token that fits your use case:
| Token | Best for | Setup |
|---|---|---|
| Fine-grained personal access token | Your own repositories | Select only the repositories the sandbox needs and grant Contents: Read. |
| GitHub App installation token | Platforms where users connect their repositories | Give the app Contents: Read. Tokens expire after 1 hour and only cover the repositories in that installation. |
| Classic personal access token | Tools that don't support fine-grained tokens | Grant the repo scope. This token can read every repository your account can access, so prefer the other two options. |
If users bring their own repositories, have them install your GitHub App, store the installation ID, and generate a fresh installation token each time you create a sandbox. This example uses @octokit/auth-app:
import { createAppAuth } from '@octokit/auth-app';
import { Sandbox } from '@vercel/sandbox';
const auth = createAppAuth({
appId: process.env.GITHUB_APP_ID!,
privateKey: process.env.GITHUB_APP_PRIVATE_KEY!,
});
export async function createSandboxForRepo(
installationId: number,
repoUrl: string,
) {
const { token } = await auth({ type: 'installation', installationId });
return Sandbox.create({
source: {
type: 'git',
url: repoUrl,
username: 'x-access-token',
password: token,
},
});
}
Pass tokens through environment variables so they stay out of logs and source code. Grant only Contents: Read, which is all cloning needs, and rotate personal access tokens on a schedule.
The Edgenivo Managed Images include common languages and tools, so most workloads don't need extra packages. To add one, run the image's package manager with sudo. The Ubuntu-based images, including the default vercel/sandbox/universal image, use apt-get. A new sandbox has no package index, so run apt-get update before you install:
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'],
sudo: true,
});
The -y flag answers yes to the installation prompt, so the command doesn't wait for confirmation on stdin. Sandboxes created with the deprecated runtime property run Amazon Linux 2023 and use dnf instead, for example dnf install -y golang. See Runtimes for the packages those runtimes include.
A persistent sandbox keeps installed packages when it stops and resumes. A new sandbox starts from its image again. To start new sandboxes with your packages already installed, snapshot a sandbox after you install them, or build a custom image from a Dockerfile.
With Sandbox CLI 4.8.0 or later, start OpenCode in a new sandbox from an interactive terminal:
sandbox create opencodeThe agent's interface opens in your terminal while its commands run in the sandbox. Sign in to Edgenivo for Sandbox access and authenticate OpenCode separately. Your local files are not copied automatically. See the CLI reference for agent options and how to reconnect or stop the sandbox.
Persistent sandboxes keep their filesystem across sessions. Create a sandbox, write a file, stop it, then resume by name and read the file back, with no snapshot ID to track and no setup to repeat.
import { Sandbox } from '@vercel/sandbox';
// First run: create a named sandbox, write a file, stop it.
const sandbox = await Sandbox.create({ name: 'my-sandbox' });
await sandbox.writeFiles([
{
path: '/vercel/sandbox/notes.txt',
content: Buffer.from('Hello from the first session.\n'),
},
]);
await sandbox.stop();
// Later, in a separate process: resume the same sandbox by name and
// read the file back. The next SDK call auto-resumes the session.
const resumed = await Sandbox.get({ name: 'my-sandbox' });
const notes = await resumed.runCommand('cat', ['/vercel/sandbox/notes.txt']);
console.log(await notes.stdout()); // Hello from the first session.
You can call Sandbox.get() from any process that has the sandbox name, for example after your script restarts or crashes. If the sandbox is still running, you reconnect to its current session. If the sandbox might not exist yet, use Sandbox.getOrCreate() to retrieve it or create it in one call.
By default, sandboxes timeout after 5 minutes. For longer tasks, set a custom timeout when creating the sandbox:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
timeout: 3 * 60 * 60 * 1000, // 3 hours
});
try {
console.log(sandbox.timeout);
} finally {
await sandbox.stop();
}
To extend a running sandbox, call extendTimeout() in TypeScript or extend_execution_time_limit() in Python:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create();
try {
await sandbox.extendTimeout(2 * 60 * 60 * 1000); // Add 2 hours
} finally {
await sandbox.stop();
}
Use sandbox extend to add time to a running session without changing the saved timeout:
sandbox extend my-sandbox 30mThe command adds 30 minutes to the current deadline. It fails if the sandbox has no running session and doesn't resume a stopped sandbox.
sandbox config timeout changes the saved timeout for future sessions. If the new value exceeds the running session's timeout, it also extends that session by the difference. Lowering the saved timeout doesn't shorten the running session's deadline.
See Pricing and Limits for maximum durations by plan.
Use a detached command when you need to follow long-running output, keep a server alive, or wait for completion later.
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({ timeout: 120_000 });
try {
const command = await sandbox.runCommand({
cmd: 'bash',
args: ['-lc', 'for i in 1 2 3; do echo $i; sleep 1; done'],
detached: true,
});
for await (const line of command.logs()) {
if (line.stream === 'stdout') {
process.stdout.write(line.data);
} else {
process.stderr.write(line.data);
}
}
const finished = await command.wait();
console.log(finished.exitCode);
} finally {
await sandbox.stop();
}
The CLI has no detached mode: sandbox exec streams output while the command runs and returns when it exits. To start a command and return right away, use detached: true in the SDK.
Use file APIs when your local application needs to send input files to the sandbox and retrieve a build output.
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create();
try {
await sandbox.mkDir('src');
await sandbox.writeFiles([
{
path: 'src/build-artifact.js',
content: Buffer.from(
"import { mkdir, writeFile } from 'node:fs/promises';\n\n" +
"await mkdir('dist', { recursive: true });\n" +
"await writeFile('dist/output.txt', 'hello from sandbox\\n');\n"
),
},
]);
const result = await sandbox.runCommand('node', ['src/build-artifact.js']);
if (result.exitCode !== 0) {
throw new Error(await result.stderr());
}
await sandbox.downloadFile(
{ path: 'dist/output.txt' },
{ path: './artifacts/dist/output.txt' },
{ mkdirRecursive: true }
);
} finally {
await sandbox.stop();
}
Use snapshots after dependency installation or environment setup so future sandboxes start from the same filesystem state. With persistent sandboxes, snapshots are also created automatically every time the sandbox stops; for manual checkpoints, call snapshot() explicitly.
To spawn fresh children from another sandbox's current snapshot without tracking IDs manually, use Sandbox.fork. The fork inherits the source's config and is seeded from its latest snapshot:
import { Sandbox } from '@vercel/sandbox';
const child = await Sandbox.fork({
sourceSandbox: 'my-base-sandbox',
persistent: false,
});
import { Sandbox } from '@vercel/sandbox';
const MIN_SNAPSHOT_EXPIRATION_MS = 24 * 60 * 60 * 1000;
let snapshotId = '';
const sandbox = await Sandbox.create({ runtime: 'node24' });
try {
await sandbox.writeFiles([
{ path: 'config.json', content: Buffer.from('{"env": "prod"}') },
]);
const snapshot = await sandbox.snapshot({
expiration: MIN_SNAPSHOT_EXPIRATION_MS,
});
snapshotId = snapshot.snapshotId;
} catch (error) {
await sandbox.stop();
throw error;
}
const restored = await Sandbox.create({
source: { type: 'snapshot', snapshotId },
timeout: 120_000,
});
try {
const result = await restored.runCommand('cat', ['config.json']);
console.log(await result.stdout());
} finally {
await restored.stop();
}
Connect to a running sandbox for interactive debugging with an SSH-like experience:
sandbox connect <name>Once connected, you have full shell access to inspect logs, check processes, and explore the filesystem.
See CLI Reference for all options.
View your sandboxes in the Sandboxes dashboard. For each project, you can see:
- Total sandboxes created
- Currently running sandboxes
- Stopped sandboxes
- Command history and sandbox URLs
To list sandboxes from the terminal, run sandbox list. It shows running sandboxes by default, and --all includes stopped ones:
sandbox list --allTrack compute usage across projects in the Usage dashboard, which measures:
- Sandbox Provisioned Memory: Memory allocated to your sandboxes
- Sandbox Data Transfer: Data your sandboxes send to the internet, plus all traffic to and from exposed ports, is billable. Data your sandboxes download from the internet is free
- Sandbox Active CPU: CPU time consumed
- Sandbox Creations: Number of sandboxes created
- Snapshot Storage: Sandbox snapshot storage
There are four ways to stop a sandbox:
- Go to Sandboxes in Observability.
- Select your sandbox.
- Click Stop Sandbox.
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create();
try {
// Run your workflow here.
} finally {
await sandbox.stop();
}
Run sandbox stop with the name of the sandbox you want to stop:
sandbox stop my-sandboxSandboxes stop automatically when their timeout expires. The default is 5 minutes.
Stopping a persistent sandbox ends the current session but keeps the sandbox so it can be resumed later. To remove the sandbox along with all of its sessions, delete it. This cannot be undone. Deleting a sandbox keeps its snapshots, which stay available until they expire or you delete them.
The dashboard is the safest way to delete a single sandbox interactively. It requires you to type the sandbox name plus a verification phrase before the deletion goes through:
- Go to Sandboxes in Observability and select the sandbox you want to delete.
- Scroll to the Delete Sandbox section at the bottom of the detail page.
- Click Delete Sandbox.
- In the confirmation modal, type the sandbox name and the verification phrase
delete my sandbox, then click Delete Sandbox.
Use sandbox.delete() from the JS SDK to remove the sandbox in code. This is useful for cleanup at the end of a job or when reacting to an event:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.get({ name: 'my-sandbox' });
await sandbox.delete();Run sandbox remove for ad-hoc cleanup or to script deletion alongside other CLI commands:
sandbox remove my-sandboxExecute AI-generated code safely
Learn how to run code generated by AI models in an isolated sandbox environment.
Use with Claude Agent SDK
Learn how to deploy Claude's Agent SDK in Edgenivo Sandbox for secure and isolated execution of AI-powered code generation and autonomous agent tasks.
Run AI-generated code
How to execute untrusted, AI-generated code inside Edgenivo Sandbox - an isolated, ephemeral environment.
Run OpenClaw in Edgenivo Sandbox
Learn how to run OpenClaw in Edgenivo Sandbox for secure and isolated execution.
Run OpenCode securely with the Edgenivo Sandbox
Learn how to run OpenCode securely with the Edgenivo Sandbox to build your own background coding agent
Run Cursor Cloud Agents in Edgenivo Sandbox
Learn how to run Cursor Cloud Agents in Edgenivo Sandbox instead of Cursor's hosted machines.
Was this helpful?