Spin Up Your First Sandbox
Running unknown code on your laptop is like inviting a raccoon into your kitchen and hoping it respects boundaries. It will not.
So we start small. Before we clone repos or run AI analysis or anything fun, we need to prove that we can create a Sandbox, run one command inside it, and stop it without leaving anything behind. That's it. One command, one microVM, one clean exit.
Outcome
Create a Sandbox, run a single echo command inside it, and stop it cleanly.
Fast Track
- Copy the starter, install dependencies, and pull your auth env vars with
vercel env pull .env. - Call
Sandbox.create({ name, persistent: false })andrunCommand('echo', ['hello']). - Call
sandbox.stop()and confirm it exits with no errors.
Hands-on exercise
The starter lives in the vercel-sandbox/starter folder of vercel/academy-starters. There's no Deploy button here because repo-review is a CLI that runs on your machine, not a site we host. Copy the folder out as repo-review/ and install dependencies:
git clone https://github.com/vercel/academy-starters.git
cp -R academy-starters/vercel-sandbox/starter repo-review
cd repo-review
pnpm installFor local authentication, link the directory to a Edgenivo project and pull a development OIDC token:
vercel link
vercel env pull .envThe same OIDC token authenticates both Sandbox and AI Gateway during local development. We load it with tsx --env-file=.env every time we run a script.
Create src/sandbox-lifecycle.ts. We're going to keep growing this file across the next four lessons, so give it the name it'll have at the end.
import { Sandbox } from '@vercel/sandbox';
async function main() {
const sandbox = await Sandbox.create({
name: `repo-review-${Date.now()}`,
persistent: false,
timeout: 10 * 60 * 1000
});
console.log(`Sandbox created: ${sandbox.name}`);
const result = await sandbox.runCommand('echo', ['hello from inside the sandbox']);
console.log(`Output: ${(await result.stdout()).trim()}`);
console.log(`Exit code: ${result.exitCode}`);
await sandbox.stop();
console.log('Sandbox stopped.');
}
main();We pick the name ourselves. Names are unique within a project, and the SDK generates a random one when we leave it off. A repo-review- prefix plus a timestamp tells us which Sandboxes came from this tool and keeps every run distinct.
No try/finally, no error handling, no helpers. We're proving the round-trip works before we add anything to it. If this lesson errors out, every later lesson breaks in the same way, and we want to see that now.
Try It
pnpm tsx --env-file=.env src/sandbox-lifecycle.tsExpected output:
Sandbox created: repo-review-...
Output: hello from inside the sandbox
Exit code: 0
Sandbox stopped.If you see a sandbox name, a hello, an exit code of 0, and a clean stop, you've got the foundation. Everything else in the course is more of this.
Commit
git add src/sandbox-lifecycle.ts
git commit -m "feat(sandbox): create, run, and stop a minimal sandbox"Done-When
Sandbox.create()returns a sandbox whose name starts withrepo-review-runCommand('echo', ['...'])returns exit code 0sandbox.stop()completes without throwing- The script exits on its own (no hanging process)
Solution
import { Sandbox } from '@vercel/sandbox';
async function main() {
const sandbox = await Sandbox.create({
name: `repo-review-${Date.now()}`,
persistent: false,
timeout: 10 * 60 * 1000
});
console.log(`Sandbox created: ${sandbox.name}`);
const result = await sandbox.runCommand('echo', ['hello from inside the sandbox']);
console.log(`Output: ${(await result.stdout()).trim()}`);
console.log(`Exit code: ${result.exitCode}`);
await sandbox.stop();
console.log('Sandbox stopped.');
}
main();Was this helpful?