vsnap
Build Vercel Sandbox snapshots from declarative .snap files.
.snap files are declarative build recipes -- like a Dockerfile, but for Vercel Sandbox environments. Each directive is a single line that says what to do: install packages, run commands, copy files, write configs. No programming language, no boilerplate. Just a clean, sequential recipe.
Installation
uv add vsnap
# or: pip install vsnap
Requires Python 3.10+ and a Vercel project with sandbox access.
Quick Start
Create a .snap file:
# my-app.snap
runtime node22 {
env NODE_ENV development
expose 3000
}
workdir /vercel/sandbox/my-app
install git jq
run "npm install"
run "npm run build"
Build a snapshot:
vsnap build my-app.snap
The CLI auto-loads .env.local for Vercel OIDC credentials. Run vercel env pull .env.local once to set up.
CLI
vsnap build <file.snap> # Build and snapshot (streams output by default)
vsnap build <file.snap> -q # Quiet mode (suppress streaming output)
vsnap build <file.snap> --dry-run # Parse and validate only, print the build plan
vsnap build <file.snap> --dry-run --output json # Machine-readable build plan
vsnap build <file.snap> --output json # JSON build result (agent-friendly)
vsnap build <file.snap> --env KEY=VALUE # Extra env vars (repeatable)
vsnap build <file.snap> --token <token> # Vercel API token (overrides .env.local)
DSL Reference
Source Directives
Every .snap file starts with a source directive that defines where the sandbox comes from. Exactly one is required.
runtime -- From a runtime image
runtime node22
runtime node22 {
vcpus 2
timeout 600000
env NODE_ENV production
env CI true
expose 3000 9229
network allow-all
}
Valid runtimes: node22, node24, python3.13
snapshot -- From an existing snapshot
snapshot snap_abc123
snapshot snap_abc123 {
runtime node22
vcpus 2
}
git -- From a git repository
git https://github.com/user/repo.git {
runtime node22
depth 1
revision main
}
tarball -- From an archive
tarball https://example.com/archive.tar.gz {
runtime node22
}
Source block sub-directives
| Sub-directive | Description |
|---|---|
vcpus <n> |
Number of vCPUs |
timeout <ms> |
Sandbox lifetime in ms (default: 30 min) |
env <KEY> <VALUE> |
Creation-time environment variable (repeatable) |
expose <port> [<port> ...] |
Ports to expose |
network allow-all | deny-all |
Network policy |
network { allow ...; subnet_allow ...; subnet_deny ... } |
Custom network policy |
runtime <name> |
Runtime override (snapshot/git/tarball only) |
depth <n> |
Git clone depth (git only) |
revision <ref> |
Git branch/tag/commit (git only) |
username <str> |
Git auth username (git only) |
password <str> |
Git auth password (git only) |
Step Directives
Steps execute in order, top to bottom. They appear after the source directive.
run -- Execute a shell command
run "npm install"
run "systemctl start nginx" {
sudo
}
run "npm run build" {
env NODE_ENV production
cwd /opt/build
}
# Multi-command block (each line = separate step)
run {
npm install
npm run build
npm prune --production
}
| Sub-directive | Description |
|---|---|
sudo |
Run with sudo |
cwd <path> |
Working directory override (this command only) |
env <KEY> <VALUE> |
Per-command env var (repeatable) |
script -- Execute a multi-line script
# From a local file
script ./scripts/setup.sh
# Inline (escape sequences in double quotes)
script "echo hello\necho world"
# Heredoc
script <<EOF
set -e
curl -fsSL https://example.com/install.sh | bash
ln -sf /usr/local/lib/tool /usr/local/bin/tool
EOF
# With options
script ./scripts/deploy.sh {
sudo
shell python3
cwd /opt/build
env CI true
}
| Sub-directive | Description |
|---|---|
sudo |
Run with sudo |
shell <name> |
Shell interpreter (default: bash) |
cwd <path> |
Working directory override |
env <KEY> <VALUE> |
Per-script env var (repeatable) |
install / remove / update -- System packages
install git curl jq make
remove nano
update curl
Uses dnf on Amazon Linux 2023 (the sandbox OS). Runs with sudo automatically.
copy -- Upload local files into the sandbox
copy ./assets/config.json /vercel/share/config.json
copy ./scripts/start.sh /usr/local/bin/start.sh {
mode 0755
}
copy ./assets/config/ /vercel/share/.config/
Paths are relative to the .snap file's directory.
file -- Write inline content to a file
file /etc/config.json '{"port": 3000}'
file /vercel/sandbox/config/app.json <<EOF
{
"port": 3000,
"debug": false
}
EOF
file /opt/script.sh "#!/bin/bash\nexec node server.js" {
mode 0755
}
Default mode is 0644.
download -- Download a remote file
download https://example.com/tool.tar.gz {
dest /usr/local/bin
extract
checksum sha256:abc123...
}
download https://example.com/config.json {
dest /etc/app/config.json
}
| Sub-directive | Description |
|---|---|
dest <path> |
Destination path in sandbox (required) |
extract |
Extract archive (tar/zip) after download |
checksum <algo>:<hash> |
Verify checksum |
workdir -- Set the working directory
workdir /vercel/sandbox/my-app
run "npm install" # runs in /vercel/sandbox/my-app
Creates the directory implicitly.
mkdir -- Create a directory
mkdir /vercel/sandbox/my-app/src
env -- Persist an environment variable
env CUSTOM_VAR value
env NODE_ENV production
Writes to /etc/environment so the variable survives snapshot restore. This is different from env inside a source block, which sets creation-time env vars.
Syntax
.snap files use a Caddyfile-inspired syntax:
- One directive per line. Each line starts with a directive name followed by arguments.
- Bare words. No quotes needed for simple values. Use double or single quotes for values with spaces.
- Blocks. Use
{ }for sub-directives (options) or multi-line content. - Heredocs. Use
<<MARKER ... MARKERfor multi-line file content and scripts. - Comments.
#to end of line. - Booleans by presence.
sudomeans true. Absence means false.
Quoting
run "echo hello world" # double quotes for spaces
run 'echo hello world' # single quotes also work
run "echo \"quoted\"" # escape quotes in double-quoted strings
script "line1\nline2" # \n = newline, \t = tab, \\ = backslash
install git jq # bare words when no spaces needed
Escape sequences (double quotes only)
| Escape | Result |
|---|---|
\n |
Newline |
\t |
Tab |
\\ |
Backslash |
\" |
Double quote |
Metadata
Release files for vsnap 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vsnap-0.1.0.tar.gz | 73.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vsnap-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 99.1 kB
Release files / vsnap-0.1.0.tar.gz
| Download URL | vsnap-0.1.0.tar.gz |
|---|---|
| Size | 73.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
117d59ccbe4ce4f58b80699f7b273da58bf899bf899aa7c54090c491f916a720
|
|
BLAKE2b-256 checksum How to use checksums |
a933efe5490ce542192cda32c7bcca94dbc98413532f0c2b2d46850d2868eba4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 9, 2026.
Transparency logRelease files / vsnap-0.1.0-py3-none-any.whl
| Download URL | vsnap-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b30cfccac4330fefa0438e057b6942e5dee1bbc209947d80adb2f4b34b15b96c
|
|
BLAKE2b-256 checksum How to use checksums |
d804ea75968a4fdbf39a0f74f21642eda0bb5241425262ff5fe3cdaf89f39ca2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 9, 2026.
Transparency log