Skip to main content

Build Vercel Sandbox snapshots from declarative .snap files

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vsnap-0.1.0.tar.gz (73.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vsnap-0.1.0-py3-none-any.whl (25.3 kB view details)

Uploaded Python 3

File details

Details for the file vsnap-0.1.0.tar.gz.

File metadata

  • Download URL: vsnap-0.1.0.tar.gz
  • Upload date:
  • Size: 73.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vsnap-0.1.0.tar.gz
Algorithm Hash digest
SHA256 117d59ccbe4ce4f58b80699f7b273da58bf899bf899aa7c54090c491f916a720
MD5 06e0235a83838daedbeb66f3647da2b2
BLAKE2b-256 a933efe5490ce542192cda32c7bcca94dbc98413532f0c2b2d46850d2868eba4

See more details on using hashes here.

Provenance

The following attestation bundles were made for vsnap-0.1.0.tar.gz:

Publisher: release.yml on gscho/vsnap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vsnap-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: vsnap-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 25.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vsnap-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b30cfccac4330fefa0438e057b6942e5dee1bbc209947d80adb2f4b34b15b96c
MD5 16bb2247aa6e5572eb44df47a3e5f1f6
BLAKE2b-256 d804ea75968a4fdbf39a0f74f21642eda0bb5241425262ff5fe3cdaf89f39ca2

See more details on using hashes here.

Provenance

The following attestation bundles were made for vsnap-0.1.0-py3-none-any.whl:

Publisher: release.yml on gscho/vsnap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page