---
title: "Sandbox Snapshots for Speed"
description: "The pipeline's slowest step is installing dependencies. Sandbox snapshots preserve a prepared filesystem so repeated runs can reuse a toolchain or warmed package cache. Add snapshot-ID restoration with an explicit unconfigured fallback."
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-08-21T16:55:07.561Z"
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 reuse prepared filesystem state. A snapshot captures a running Sandbox's files and installed packages; it is distinct from the container image used to boot the Sandbox. Restoring one can skip toolchain setup or reuse a warmed package cache.

A snapshot containing one repository's `node_modules` will not generally accelerate an unrelated repository. Aim for reusable toolchains and package caches, or create a snapshot specifically for a stable repository.

## Outcome

Update `runSandboxLifecycle` to create the Sandbox from a configured snapshot ID. If no ID is configured, create a default throwaway Sandbox. If a configured ID is invalid, surface the error instead of disguising every failure as a missing snapshot.

## Fast Track

1. Read a snapshot ID from `SANDBOX_SNAPSHOT_ID`.
2. Restore with `source: { type: 'snapshot', snapshotId }`.
3. Use default creation only when the env var is absent.

## 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_ID = process.env.SANDBOX_SNAPSHOT_ID;

async function createSandbox(): Promise<{ sandbox: Sandbox; usedSnapshot: boolean }> {
  if (!SNAPSHOT_ID) {
    console.warn('SANDBOX_SNAPSHOT_ID is not set; using a default Sandbox.');
    const sandbox = await Sandbox.create({ persistent: false, timeout: 10 * 60 * 1000 });
    return { sandbox, usedSnapshot: false };
  }

  const sandbox = await Sandbox.create({
    source: { type: 'snapshot', snapshotId: SNAPSHOT_ID },
    persistent: false,
    timeout: 10 * 60 * 1000
  });
  return { sandbox, usedSnapshot: true };
}

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

export type LifecycleResult = {
  sandboxName: 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', '--depth', '1', repoUrl, 'repo']);
    if (clone.exitCode !== 0) {
      throw new Error(`Clone failed: ${await clone.stderr()}`);
    }

    const files: Array<{ path: string; content: string }> = [];
    for (const fullPath of INTERESTING_PATHS) {
      const content = await sandbox.readFileToBuffer({ path: fullPath });
      if (content) {
        files.push({
          path: fullPath.replace(/^repo\//, ''),
          content: content.toString('utf8')
        });
      }
    }

    const pm = await detectPackageManager(sandbox);
    const install = await sandbox.runCommand(pm.install);
    if (install.exitCode !== 0) {
      throw new Error(`Install failed: ${await install.stderr()}`);
    }
    const test = await sandbox.runCommand(pm.test);

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

Three things to flag.

The snapshot ID is configurable via `SANDBOX_SNAPSHOT_ID`. Snapshot IDs, rather than user-defined names, are what `Sandbox.create({ source })` restores.

The fallback is only for an absent configuration. A configured but expired, deleted, or inaccessible snapshot should fail loudly so authentication and service errors are not misreported.

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."

To create one, prepare a Sandbox and call `snapshot()`. Snapshotting stops that Sandbox automatically:

```ts
const base = await Sandbox.create({ persistent: false });
await base.runCommand('corepack', ['enable']);
const snapshot = await base.snapshot({ expiration: 14 * 24 * 60 * 60 * 1000 });
console.log(snapshot.snapshotId);
```

Snapshots expire after 30 days by default. Pass an explicit expiration when your retention needs differ.

\*\*Warning: Troubleshooting: snapshot restore fails\*\*

Confirm `SANDBOX_SNAPSHOT_ID` contains a current snapshot ID and that your Vercel credentials can access its project. Remove the variable only when you intentionally want default creation; don't catch every restore error as a fallback.

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

Creating a snapshot includes the setup run and storage. Reuse it enough to justify that work, and measure the actual benefit for your repository mix.

## Try It

Run without `SANDBOX_SNAPSHOT_ID`:

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

Expected output:

```txt
SANDBOX_SNAPSHOT_ID is not set; using a default Sandbox.
Reviewing https://github.com/vercel/examples...
  ⏱  sandbox lifecycle: 24180ms
  ⏱  ai analysis: 7240ms

Total: 31420ms
```

After exporting a valid snapshot ID and running again:

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

Total: 16600ms
```

The unconfigured warning is gone. Your measured improvement depends on what the snapshot contains and how closely the reviewed repo matches it. AI analysis time should remain roughly the same because snapshots do not accelerate 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({ source: { type: 'snapshot', snapshotId } })` restores a configured snapshot
- [ ] Default creation runs only when `SANDBOX_SNAPSHOT_ID` is absent
- [ ] Invalid configured snapshot errors remain visible
- [ ] `usedSnapshot` flag is returned in `LifecycleResult`
- [ ] `SANDBOX_SNAPSHOT_ID` selects the snapshot to restore

## Solution

```ts title="src/sandbox-lifecycle.ts (helper)"
const SNAPSHOT_ID = process.env.SANDBOX_SNAPSHOT_ID;

async function createSandbox(): Promise<{ sandbox: Sandbox; usedSnapshot: boolean }> {
  if (!SNAPSHOT_ID) {
    console.warn('SANDBOX_SNAPSHOT_ID is not set; using a default Sandbox.');
    const sandbox = await Sandbox.create({ persistent: false, timeout: 10 * 60 * 1000 });
    return { sandbox, usedSnapshot: false };
  }

  const sandbox = await Sandbox.create({
    source: { type: 'snapshot', snapshotId: SNAPSHOT_ID },
    persistent: false,
    timeout: 10 * 60 * 1000
  });
  return { sandbox, usedSnapshot: true };
}
```


---

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