Skip to main content
A snapshot is a saved state that a sandbox boots from. The environment is already set up inside it, so booting skips installation entirely.

Available snapshots

A name resolves to the first entry carrying it, so vsnap-base gives you the 256 MB one. To pin the other, pass its id. Both columns are accepted anywhere a snapshot is named. vsnap-base:latest is the default. Pass any other:
uv is preinstalled in every snapshot, so more Python packages can be added with uv pip install --system <package>. In a browser that reaches PyPI because PyPI sends CORS headers; apk add does not work there, because Alpine’s mirrors do not.

Pulling ahead of time

Sandbox.create() pulls whatever it needs, so there is usually nothing to do. Pull explicitly when you want the download to happen at a different moment from the run: warming a container image, a CI setup step, or a browser that should fetch during a loading screen rather than on the first click.
It is a no-op when the snapshot is already cached and its digest matches, so it is safe to call on every start. onProgress reports bytes as they arrive, which is what you want behind a progress bar in a browser:
Snapshots are transferred compressed and stored decompressed, so total is the download size rather than the space it ends up taking.

Snapshot API

catalog, resolve and pullSnapshot all take { registryUrl?, apiKey? }.

Private snapshots

A snapshot you built in the console is not in the public catalogue. Pass an API key to reach it:
VPOD_API_KEY is read from the environment when apiKey is not passed, so on a server you normally set the variable and leave the option out.
The private and public catalogues replace each other rather than merging. With a key in play, vsnap-base and the other public snapshots are no longer resolvable by name, so a globally exported VPOD_API_KEY will break a Sandbox.create() that relied on the default snapshot.
In Node the key must be a secret key (vpod_sk_). In a browser it must be a publishable key (vpod_pk_), which is restricted to an allowlist of origins; the SDK refuses a secret key there rather than letting you ship it to devtools. See Private snapshots.

Where the cache lives

The first Sandbox.create() downloads a snapshot and keeps it: on disk in Node, in origin-private storage in a browser. Later runs boot from the copy. Snapshots are stored compressed, so a 256 MB snapshot occupies well under a hundred megabytes. Still enough to be worth showing people.
clear() returns the number of bytes it freed and keeps suspended sandboxes, which live alongside the snapshots. Pass { instances: true } to drop those too. The next Sandbox.create() downloads again.
In Node the cache directory is shared with the CLI and the Python SDK. clear() removes only what it downloaded itself and leaves anything it could not fetch again, such as a snapshot you built locally. cached() still lists everything that is there, so the two can disagree, and that is deliberate.

Storage in a browser

Origin-private storage is site data rather than the HTTP cache, so the browser’s “clear cached images and files” does not touch it. A browser is also free to evict it when the disk fills up, which costs a re-download and nothing else.
Firefox prompts the user when you request persistence, so call it from something they clicked rather than on page load.