Skip to main content

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 ... MARKER for multi-line file content and scripts.
  • Comments. # to end of line.
  • Booleans by presence. sudo means 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)

Source distribution for vsnap 0.1.0
File Size Uploaded
vsnap-0.1.0.tar.gz 73.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vsnap 0.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page