JSON-config-driven runner for Vercel Sandbox microVMs. Define your setup and run steps in a JSON file, and the runner handles snapshot caching, cost tracking, and artifact downloads.
pnpm install
vercel link && vercel env pull # creates .env.local with OIDC credentials
# Run a config
node --env-file .env.local --experimental-strip-types ./run-sandbox.ts opencode-sandbox.json├── sandbox-utils.ts # Core library: snapshot management, cost tracking, orchestrator
├── run-sandbox.ts # CLI — takes any JSON config as argument
├── opencode-sandbox.json # Config: OpenCode CLI + OpenCode SDK
└── scripts/ # Script files referenced by configs
└── verify-opencode.mjs
Each config file defines a sandbox and an ordered list of steps:
Runs a shell command inside the sandbox.
{
"type": "command",
"name": "Install CLI", // display name for logging
"phase": "setup", // "setup" or "run"
"cmd": "npm",
"args": ["install", "-g", "my-package"],
"sudo": true, // optional
"env": { // optional — "$VAR" resolves from process.env
"API_KEY": "$MY_API_KEY"
}
}Uploads and runs a script file inside the sandbox. You can reference a local file or provide inline content:
// Reference a file (recommended)
{
"type": "script",
"name": "Verify SDK",
"phase": "run",
"file": "scripts/verify.mjs"
}
// Inline script (useful for one-liners)
{
"type": "script",
"name": "Quick check",
"phase": "run",
"script": "console.log('hello from sandbox');",
"filename": "check.mjs" // optional remote filename
}When using file, the remote filename defaults to the file's basename. Both .mjs, .ts, and .py files work depending on the sandbox runtime.
setup— runs only on fresh sandbox creation. Skipped when restoring from a cached snapshot. The stepnameis tracked in the snapshot's installed-packages list.run— always executes, regardless of snapshot state.
Download files from the sandbox to your local machine after all steps complete:
{
"download": [
{
"remotePath": "/vercel/sandbox/output.md",
"localPath": "./output/output.md"
}
]
}Any env value prefixed with $ is resolved from process.env at runtime:
"env": { "API_KEY": "$MY_API_KEY" }
// resolves to process.env.MY_API_KEY (or "" if unset)run-sandbox.ts my-config.json
→ Parse JSON config
→ Start cost tracker
→ Create sandbox (or restore from cached snapshot)
→ Iterate steps[] in order:
[setup + fresh] → execute, track in installed packages
[setup + snapshot] → skip (log message)
[run] → always execute
→ Download artifacts
→ [fresh] Create snapshot for next run / [snapshot] Stop sandbox
→ Print cost summary
- Node.js v22+
- A Vercel account with a linked project
- pnpm (or npm)
- Create a JSON file following the schema above
- Add any script files your steps reference under
scripts/(or anywhere) - Run it:
node --env-file .env.local --experimental-strip-types ./run-sandbox.ts my-config.json| Option | Default | Notes |
|---|---|---|
runtime |
node22 |
Available: node24, node22, python3.13 |
timeout |
10m |
Max: 45min (Hobby), 5h (Pro/Enterprise) |
vcpus |
2 |
CPU cores allocated to the VM |
{ "label": "my-sandbox", // snapshot label (used for caching) "vcpus": 2, // CPU cores (default: 2) "timeout": "10m", // ms-parseable duration (default: "10m") "runtime": "node22", // "node22", "node24", "python3.13" (default: "node22") "steps": [ /* ... */ ], // executed in order "download": [ /* ... */ ] // optional artifact downloads }