Bootstrapping a sandbox — cloning the repo, installing dependencies, running setup — is the expensive part of a run. The lifecycle config lets you pay that cost once and reuse the result: keep one sandbox per thread, snapshot it after setup, and resume instead of re-bootstrapping on the next run.
import { defineSandbox, defineWorkspace, githubRepo } from '@tanstack/ai-sandbox'
import { dockerSandbox } from '@tanstack/ai-sandbox-docker'
const sandbox = defineSandbox({
id: 'repo-agent',
provider: dockerSandbox({ image: 'node:22' }),
workspace: defineWorkspace({
source: githubRepo({ repo: 'owner/app' }),
setup: ['corepack enable', 'pnpm install'],
}),
lifecycle: {
reuse: 'thread', // one sandbox per threadId ('none' = fresh per run)
snapshot: 'after-setup', // snapshot once bootstrapped (provider-permitting)
keepAlive: '30m', // hint to keep the sandbox warm between runs
destroyOnComplete: false, // keep it for the next run
},
})| Field | What it controls |
|---|---|
| reuse | 'thread' keeps one sandbox per threadId; 'none' provisions a fresh sandbox per run. |
| snapshot | 'after-setup' snapshots the workspace once bootstrap finishes, on snapshot-capable providers. |
| keepAlive | Duration hint (e.g. '30m') to keep the sandbox warm between runs instead of tearing it down. |
| destroyOnComplete | When false, the sandbox survives the run so the next one can resume it. |
| snapshotMaxAge | Duration (e.g. '24h') after which a stored snapshot is treated as stale and re-created. |
When the provider supports snapshots (e.g. Docker), bootstrap automatically takes a snapshot after setup completes. The snapshot caches the fully bootstrapped workspace — the cloned repo with dependencies installed — so subsequent runs resume from it instead of re-running the setup steps, which dramatically reduces cold-start time.
snapshot: 'after-setup' is the default whenever the provider reports snapshot support, so you usually do not set it explicitly. Providers without snapshot support (e.g. localProcessSandbox) skip the snapshot step silently.
import { defineSandbox, defineWorkspace, githubRepo } from '@tanstack/ai-sandbox'
import { dockerSandbox } from '@tanstack/ai-sandbox-docker'
const sandbox = defineSandbox({
id: 'repo-agent',
provider: dockerSandbox({ image: 'node:22' }),
workspace: defineWorkspace({
source: githubRepo({ repo: 'owner/app' }),
setup: ['corepack enable', 'pnpm install'],
}),
lifecycle: {
reuse: 'thread',
// 'after-setup' is the default when the provider supports snapshots.
snapshot: 'after-setup',
// Optional: re-create (re-bootstrap) when the snapshot is older than this.
snapshotMaxAge: '24h',
},
})snapshotMaxAge accepts a duration string ('24h', '30m', etc.). When the stored snapshot is older than the limit, the sandbox treats it as stale and re-creates from scratch — re-running setup and capturing a new snapshot. Leave it unset to keep snapshots indefinitely.
A sandbox is keyed by a compound sandboxInstanceKey:
sandboxInstanceKey = hash(threadId + sandbox.id + provider + workspaceHash + tenant?)Because the key folds in the workspace hash, the provider, and an optional tenant, changing any input that would invalidate a cached environment — the repo, the setup steps, the provider image, or the tenant — produces a different key. That means you safely start a fresh sandbox rather than resuming a stale one whose snapshot no longer matches your config. Keep those inputs stable across runs to keep hitting the same warm sandbox.
When a run needs a sandbox, the layer resolves it in this order:
Resume the running sandbox — if a live sandbox already exists for the key.
Restore the latest snapshot — recreate from the most recent snapshot, skipping setup.
Create fresh and bootstrap — clone, install, run setup, then snapshot.
Each step falls through to the next only when the prior one is unavailable. This is what turns a warm thread into a near-instant start, and a cold one into a full bootstrap.
Which providers support durable disk, snapshots, and resume-by-id is listed on Providers.
Providers without durable disk or snapshots (e.g. ephemeral containers) re-create and re-bootstrap under the same identity: the sandboxInstanceKey stays stable, but every run pays the bootstrap cost because there is nothing durable to resume.