---
title: "Sandbox Snapshots for Speed"
description: "The pipeline's slowest step is installing dependencies. Sandbox snapshots let us bake in `pnpm` / `npm` / `yarn` and skip a chunk of that work. We add snapshot support to the lifecycle with a graceful fallback for when the snapshot doesn't exist."
canonical_url: "https://vercel.com/academy/vercel-sandbox/sandbox-snapshots-for-speed"
md_url: "https://vercel.com/academy/vercel-sandbox/sandbox-snapshots-for-speed.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-05-17T23:49:50.424Z"
content_type: "lesson"
course: "vercel-sandbox"
course_title: "Vercel Sandbox"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Sandbox Snapshots for Speed

# Use Snapshots to Skip the Cold Start

The benchmark numbers from 5.1 told us where the time goes. Most of it lives inside `pnpm install`, which makes sense because cold Sandboxes start with no `node_modules` and have to fetch everything from scratch.

Snapshots are how we cheat. A snapshot is a pre-built Sandbox image that already has whatever you put in it (a global pnpm install, common system tools, a cached node\_modules for a popular framework). Boot from a snapshot, and you skip all the work that went into building it.

## Outcome

Update `runSandboxLifecycle` to create the Sandbox from a named snapshot, and fall back to default creation with a warning when the snapshot doesn't exist.

## Fast Track

1. Add a `SNAPSHOT_NAME` constant (or read from env).
2. Try `Sandbox.create({ snapshot: SNAPSHOT_NAME })` first.
3. Catch the missing-snapshot error and fall back to plain `Sandbox.create()`.

## Hands-on exercise

Open `src/sandbox-lifecycle.ts`. We're going to extract Sandbox creation into a small helper:

```ts
import { Sandbox } from '@vercel/sandbox';
import { detectPackageManager } from './test-runner';

const INTERESTING_PATHS = [
  'repo/package.json',
  'repo/src/index.ts',
  'repo/src/app.ts',
  'repo/lib/auth.ts'
];

const SNAPSHOT_NAME = process.env.SANDBOX_SNAPSHOT ?? 'repo-review-base';

async function createSandbox(): Promise<{ sandbox: Sandbox; usedSnapshot: boolean }> {
  try {
    const sandbox = await Sandbox.create({ snapshot: SNAPSHOT_NAME });
    return { sandbox, usedSnapshot: true };
  } catch (error) {
    console.warn(
      `Snapshot "${SNAPSHOT_NAME}" unavailable; falling back to default creation.`,
      error instanceof Error ? error.message : error
    );
    const sandbox = await Sandbox.create();
    return { sandbox, usedSnapshot: false };
  }
}

export type TestResult = {
  exitCode: number;
  stdout: string;
  stderr: string;
  packageManager: string;
};

export type LifecycleResult = {
  sandboxId: string;
  usedSnapshot: boolean;
  cloneExitCode: number;
  files: Array<{ path: string; content: string }>;
  testResult: TestResult;
};

export async function runSandboxLifecycle(repoUrl: string): Promise<LifecycleResult> {
  const { sandbox, usedSnapshot } = await createSandbox();

  try {
    const clone = await sandbox.runCommand(`git clone ${repoUrl} repo`);
    if (clone.exitCode !== 0) {
      throw new Error(`Clone failed: ${clone.stderr}`);
    }

    const files: Array<{ path: string; content: string }> = [];
    for (const fullPath of INTERESTING_PATHS) {
      try {
        const content = await sandbox.readFile(fullPath);
        files.push({
          path: fullPath.replace(/^repo\//, ''),
          content
        });
      } catch {
        // file doesn't exist in this repo; skip it
      }
    }

    const pm = await detectPackageManager(sandbox);
    await sandbox.runCommand(pm.install);
    const test = await sandbox.runCommand(pm.test);

    return {
      sandboxId: sandbox.sandboxId,
      usedSnapshot,
      cloneExitCode: clone.exitCode,
      files,
      testResult: {
        exitCode: test.exitCode,
        stdout: test.stdout,
        stderr: test.stderr,
        packageManager: pm.name
      }
    };
  } finally {
    await sandbox.stop();
  }
}
```

Three things to flag.

The snapshot name is configurable via env (`SANDBOX_SNAPSHOT`) with a sensible default. This lets you experiment with different snapshots without touching the code.

The fallback is loud, not silent. If a snapshot disappears or the name changes, you want to know about it. A warning in the logs makes the cause obvious next time you wonder why the pipeline got slow again.

The `usedSnapshot` flag flows back through the result so the CLI (or the reporter we build in 5.4) can show "snapshot accelerated" in the summary. Knowing whether the snapshot path was actually taken is the difference between "we have snapshots" and "snapshots actually help."

We're not covering how to *create* a snapshot in this course. That's a one-time setup step that depends on what you want pre-baked. Check the `@vercel/sandbox` docs for the snapshot creation API.

\*\*Warning: Troubleshooting: fallback path is still slow\*\*

If the warning prints and the run still takes the full cold-start time, the fallback worked but the snapshot didn't help. That's expected the first time you run after the snapshot disappears. Recreate the snapshot to restore the speedup.

\*\*Note: Troubleshooting: snapshot creation isn't free either\*\*

The first time you create a snapshot, you pay the full cost. The next 100 runs amortize that. If you only ever run the pipeline once, snapshots aren't worth it.

## Try It

First run (or no snapshot exists):

```bash
pnpm review https://github.com/vercel/examples
```

Expected output:

```txt
Snapshot "repo-review-base" unavailable; falling back to default creation. ...
Reviewing https://github.com/vercel/examples...
  ⏱  sandbox lifecycle: 24180ms
  ⏱  ai analysis: 7240ms

Total: 31420ms
```

After you've set up the snapshot and the second run:

```txt
Reviewing https://github.com/vercel/examples...
  ⏱  sandbox lifecycle: 9420ms
  ⏱  ai analysis: 7180ms

Total: 16600ms
```

The "snapshot unavailable" warning is gone, and the lifecycle stage dropped from \~24s to \~9s. Same review, \~half the time. The AI analysis stayed roughly the same because the snapshot doesn't help with model calls.

## Commit

```bash
git add src/sandbox-lifecycle.ts
git commit -m "feat(sandbox): create from named snapshot with graceful fallback"
```

## Done-When

- [ ] `Sandbox.create({ snapshot })` is attempted first
- [ ] Missing-snapshot error is caught and logged as a warning
- [ ] Default creation runs as a fallback
- [ ] `usedSnapshot` flag is returned in `LifecycleResult`
- [ ] `SANDBOX_SNAPSHOT` env var overrides the default name

## Solution

```ts title="src/sandbox-lifecycle.ts (helper)"
const SNAPSHOT_NAME = process.env.SANDBOX_SNAPSHOT ?? 'repo-review-base';

async function createSandbox(): Promise<{ sandbox: Sandbox; usedSnapshot: boolean }> {
  try {
    const sandbox = await Sandbox.create({ snapshot: SNAPSHOT_NAME });
    return { sandbox, usedSnapshot: true };
  } catch (error) {
    console.warn(
      `Snapshot "${SNAPSHOT_NAME}" unavailable; falling back to default creation.`,
      error instanceof Error ? error.message : error
    );
    const sandbox = await Sandbox.create();
    return { sandbox, usedSnapshot: false };
  }
}
```


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
