Docs
snowglobe packages a Docker image into a folder of static files that runs it in a browser tab: real Linux on an emulated 64-bit ARM CPU, restored from a snapshot taken once your app was ready. The same site also runs headlessly, as one executable, and as a sandbox an agent drives.
Getting started
Build a site from a Dockerfile, preview it, then put the folder on any static host.
What you need
- Docker, or nothing. With Docker, snowglobe builds your Dockerfile and boots it in
a build container. Without Docker,
--builder nativebuilds from an image in a registry or a saved archive, andsnowglobe run --runtime native, bundles andsnowglobe mcprun sites with no Docker either. - An arm64 Alpine image:
FROM alpine(multi-arch; snowglobe builds it for arm64) orFROM arm64v8/alpine. On an x86 host, Docker needs QEMU's binfmt handlers to build arm64 images. Docker Desktop has them; on Linux rundocker run --privileged --rm tonistiigi/binfmt --install arm64once.
Install
Download a binary for Linux or macOS (amd64 or arm64) from the releases, or install it with Go 1.27 or later:
go install github.com/filipecabaco/snowglobe/cmd/snowglobe@latest
The first build fetches the tools image (the machine that boots and snapshots your image) from
ghcr.io/filipecabaco/snowglobe-tools. A version that hasn't been released compiles it
locally instead, once, in a few minutes. Without Docker, the first native build or run fetches this
release's snowglobe-vm and its prebuilt kernel into your cache.
Your first site
Any directory with a Dockerfile, or any image reference, builds into dist/:
snowglobe build ./my-app # a directory with a Dockerfile, or an image reference snowglobe serve dist # preview at http://localhost:8000
Whatever the image runs (its ENTRYPOINT and CMD, with its ENV and
WORKDIR) appears in a terminal on the page. dist/ is plain static files: upload it
to GitHub Pages, a CDN or any web server. There is no snowglobe server.
Everything runs on an emulated 64-bit ARM CPU, translated to WebAssembly as it runs, so expect it to be slower than native. Cold loads take seconds; commands take tens of milliseconds to a few seconds.
Building a site
A Dockerfile describes the app; labels in it, or flags, describe how snowglobe should snapshot it.
Describe it in the Dockerfile
The Python demo is this Dockerfile. Labels let an image describe itself, and flags override them:
FROM alpine:3.24.2
RUN apk add --no-cache python3
CMD ["python3"]
LABEL snowglobe.title="Python in the browser" \
snowglobe.ready=">>> " \
snowglobe.exercise='["import json, re, collections", "import asyncio; asyncio.run(asyncio.sleep(0))"]'
When the snapshot is taken
snowglobe boots the image until the app is ready, then saves the whole machine. Visitors restore that
snapshot rather than booting Linux. Tell it what "ready" looks like with --ready (or
snowglobe.ready): text the app prints, usually its prompt. Without it, the app counts as
ready once its terminal has been quiet for 5 seconds.
--cmd swaps the program without touching the image, and drops the image's
ready and exercise labels, which belonged to its own command.
Exercises and the warm pack
The guest fetches its files on demand. To hide that, every site preloads a warm.pack in
the background, filled by measuring rather than guessing:
- Boot reads: every file the guest read while booting.
- Exercise reads: after the snapshot, snowglobe types each
--exercisecommand into the app and records what it reads: the files a visitor's first commands would otherwise wait for. A command is done once the app prints its prompt again and goes quiet. - Pattern: anything matching
--warm REGEX, for files no exercise touches.
Each build prints a breakdown and writes warm-report.json. An app used over the network
rather than its console can be warmed from real traffic instead:
snowglobe run dist --name api --detach -p 5000:5000 --record-reads api-reads.json
npm test # anything that talks to 127.0.0.1:5000
snowglobe stop api && snowglobe warm dist api-reads.json
CPUs and memory
--memory MB sets the guest's RAM (512 by default). --cpus N (1 to 8) gives it
more CPUs, each in its own Web Worker on shared memory, which helps programs that use them. Browsers only
allow that on a cross-origin isolated page, which a static host can't declare, so such a site ships a
small service worker that reloads the page once on the first visit, and it can't run inside another
page's frame. A build boots on at least 4 CPUs (--build-cpus) and takes the extra ones
offline before the snapshot.
Services in the image
The guest boots with BusyBox's init and no service manager, so the app is ready seconds
after the kernel starts. Services an image enables for OpenRC do not start: start them from the
command, as the Postgres and Elixir examples' start scripts do. Slow first-time setup (an
initdb, migrations) belongs in a Dockerfile RUN, where it runs at native speed
instead of in emulation.
Building without Docker
--builder native (or SNOWGLOBE_BUILDER=native) builds from an image reference,
pulled from its registry, or an image archive (docker save, or an OCI layout from buildah,
kaniko or anything else). Nothing runs at build time either way: the image's layers are flattened, the
prebuilt kernel and boot files are added on top, and snowglobe-vm boots and snapshots it on
your machine.
snowglobe build python:3.13-alpine --builder native --ready '>>> ' docker save my-app:latest -o my-app.tar # on a machine with a builder snowglobe build my-app.tar --builder native # anywhere else
Hosting several sites, and installing them
snowglobe pool <dir> lets every site under a directory share one blob store. Blobs are
named by content, so sites built on the same base share many, and a visitor's second site reuses what
their browser cached.
snowglobe pwa <site> makes a site an installable app that keeps working offline once it
has run: a manifest, icons and a service worker caching the page, the machine and every file the run
fetched. Run it after pool.
Networking
A guest has no network unless the site asks for one, and then its only way out is the visitor's own browser.
HTTP, HTTPS and WebSockets
With --network fetch (or snowglobe.network="fetch"), the guest gets a network
card and DHCP, and every request it makes leaves as the page's own browser request. Its connections to
ports 80 and 443 end inside the machine:
- HTTP requests become a
fetch(). - HTTPS is terminated in the machine with a certificate signed by a CA made fresh for
each build, which the guest trusts, and replayed with
fetch("https://…"). Nothing else should trust that CA: its key ships with the site. - WebSockets open a browser
WebSocketto the same URL.
The browser's rules still apply. HTTP(S) only reaches servers that allow CORS (others answer with a 502 that says so), UDP goes nowhere, and other TCP only through a tunnel.
Other TCP: tunnels
A page can't open raw TCP, so a connection to a database or any port but 80 and 443 is refused unless
the site names it as a tunnel. --tunnel HOST:PORT=URL carries every connection to
HOST:PORT over a WebSocket to URL, as binary messages: what websockify, websocat
and Neon's wsproxy speak.
websocat --binary ws-l:0.0.0.0:6543 tcp:127.0.0.1:5432 # next to Postgres
snowglobe build ./my-app --network fetch --tunnel db.internal:5432=wss://db-proxy.example.com/
Credentials
Whatever the guest knows ships with the site: a password in its environment is in the snapshot, for every visitor.
Instead, put {token} in the tunnel URL and name a token endpoint with
--tunnel-token URL. The page asks it for a fresh token on every connection (natively, once
per boot), and a gateway behind the tunnel checks the token and holds the real credentials. A token only
replaces the password: what it opens is whatever the gateway lets any visitor do.
Builds and snowglobe run refuse loopback and private addresses, so a guest can't reach the
host's own services. --allow-private lets them, for a tunnel endpoint on your own machine
while developing.
Running outside a browser
The same snapshot runs on your terminal, in a container or natively, from a folder, a tarball or a deployed site's URL.
Sessions and instances
snowglobe run dist/elixir # a session; ctrl-] quits printf 'Enum.sum(1..100)\n' | snowglobe run dist/elixir snowglobe run dist/elixir --name pg --detach snowglobe exec pg 'sql "SELECT 1"' # prints only what the command printed snowglobe attach pg # join it; ctrl-] detaches snowglobe ps snowglobe stop pg
snowglobe run uses a container by default (--runtime docker: unprivileged,
read-only, memory-capped). --runtime native (or SNOWGLOBE_RUNTIME=native) runs
snowglobe-vm itself, with no Docker. Piped input is typed in one line at a time, each waiting
until the app goes quiet. Each instance starts fresh from the snapshot.
Instances share their memory
Native instances of one site don't each hold a copy of its snapshot. The first one writes the snapshot's memory to an image in the cache, and every instance after it maps that image copy-on-write: the pages no instance writes are kept once for all of them, and a page an instance writes becomes its own. It restores in a few milliseconds instead of decompressing the snapshot first.
| Ten instances of the Python run | Each, before | Each, now |
|---|---|---|
| Memory on Linux (PSS) | 111 MB | 46 MB |
| Memory on macOS (physical footprint) | 175 MB | 96 MB |
| Restore | 60–74 ms | 4–6 ms |
The image lives in ram/ under the cache (SNOWGLOBE_CACHE, by default
~/.cache/snowglobe), one per snapshot, named by its hash. It takes about as much disk as the
snapshot's memory in use (71 MB for the Python run), and images unused for two weeks go when a new one is
made. Machines forked from a template, by --fork or the daemon, already shared the
template's memory and still do; their template server maps the image too. How the image is kept from
changing under a machine is under Isolation;
SNOWGLOBE_RAM_IMAGE=off restores into memory of the instance's own.
Sites from a URL
A site doesn't have to be local. snowglobe run takes a deployed site's URL, which it
mirrors into a cache, or a tarball made with snowglobe pack, local or by URL. Downloads
happen on the host and are revalidated, so the second run starts from the cache and the sandbox stays
offline.
snowglobe run https://filipecabaco.github.io/snowglobe/python/ snowglobe pack dist/elixir -o elixir.tar.gz snowglobe run https://cdn.example.com/elixir.tar.gz
Serving from the guest
A site built with --network fetch can serve: -p HOST:GUEST forwards a port on
your machine to one in the guest. Host ports bind to 127.0.0.1 unless you give an address
(-p 0.0.0.0:8080:4000).
One executable
snowglobe bundle makes a site a single executable: snowglobe-vm with the site
appended. It needs no Docker, hypervisor or KVM, so it runs on a laptop, in CI or inside someone else's
container. Build with --sandbox for this: the guest keeps its file cache in the snapshot,
and exercises run before it.
snowglobe build examples/python --sandbox --out dist/python
snowglobe bundle dist/python -o python # Linux; --target darwin for Apple silicon
echo 'sum(i*i for i in range(10))' | ./python
A bundle whose app is a shell also runs one-shot jobs: --workspace DIR is written into a
fresh machine, --cmd runs there, and its output, exit status and out/ come back.
--serve SOCKET --jobs-root DIR restores the snapshot once and answers each job with a fork
of it, sharing the restored machine copy-on-write; --server SOCKET is its client.
./toolkit --workspace job --cmd 'sh main.sh' --env 'N=42' ./toolkit --serve /run/toolkit.sock --jobs-root /var/lib/jobs & ./toolkit --server /run/toolkit.sock --workspace /var/lib/jobs/1 --cmd 'sh main.sh'
For agents and programs
A native instance takes jobs on the guest's root shell, whatever its app is, and a run can be bounded, stopped from outside and recorded. For many machines, the daemon forks them from templates in milliseconds, scales services to zero and applies whole fleets from a file.
Commands and files: sh and cp
Pass the command to snowglobe sh as one quoted argument; its flags would otherwise be read as
snowglobe's own.
snowglobe run dist/python --name box --detach --runtime native snowglobe sh box 'python3 -c "print(6*7)"' # its output, and its exit status as ours snowglobe sh box --json 'ls /nope' snowglobe cp ./project box:/root/project # files and directories, either way snowglobe cp box:/root/project/out ./out
Each command runs with the app's environment in a fresh working directory; files elsewhere persist for
the instance's life. Out of time (300 s, or --timeout S), the command and everything it
started are killed (exit 124). What the guest sends back through cp can't write outside the
destination.
A fresh machine per command: --fork
--fork makes a detached native instance a template. It restores once, and every
snowglobe sh on it runs in a copy of that machine, forked in milliseconds and sharing its
memory. Nothing one command does is seen by the next, so sh is all a fork instance takes:
cp, exec, attach and mcp need a machine that lasts.
snowglobe run dist/python --name tpl --detach --runtime native --fork
snowglobe sh tpl 'python3 untrusted.py' # many at once, each on its own copy
snowglobe stop tpl
Templates and machines: the daemon
snowglobe daemon keeps templates (a site restored once) and the machines forked from them.
snowglobe up forks a machine from a template in tens of milliseconds. The machine shares
the template's memory, lasts until it is stopped, keeps what it writes, and has its ports forwarded
by the daemon. sh, cp, exec, attach,
ps and stop reach the daemon's machines as they reach instances.
snowglobe daemon & snowglobe template add dist/python --name py # restored once, its JIT code compiled once snowglobe up py --name web -p 8080:8000 --label app=web snowglobe up py --name worker # another machine, its own state snowglobe sh web 'python3 -m http.server 8000 &' snowglobe ps snowglobe stop worker snowglobe template rm py # and its machines
The API is HTTP/JSON on a Unix socket in the user cache (curl --unix-socket works):
/v1/templates, /v1/machines, /v1/machines/{name}/jobs and
/v1/templates/{name}/jobs (a job on a fresh fork). --listen ADDR --token-file F
also serves it over TCP, to requests bearing the token, and SNOWGLOBE_DAEMON=http://HOST:PORT
with SNOWGLOBE_DAEMON_TOKEN points the CLI at it. A daemon started again takes back the
templates and machines that are still running.
Limits and lifecycle
snowglobe daemon --memory 8G caps what its template servers and machines take together.
A machine that doesn't fit waits up to --wait (30 s) for room, then is refused with the
reason. Linux measures each process's proportional share; macOS its resident memory, which counts
pages shared with the template in full, so the cap is reached sooner. No cap unless asked.
up --ttl 1h stops a machine after that long, whatever it is doing.
template add --idle 30m stops a template's server after that long without a machine; the
next up (or job) restores it first, in about a second.
snowglobe daemon --memory 8G --wait 30s & snowglobe template add dist/python --name py --idle 30m snowglobe up py --ttl 1h
Events and top
snowglobe top shows the daemon live: memory against its budget, templates (awake or
asleep), services with their replicas and open connections, machines with what each takes and when it
stops, and the latest events. snowglobe events streams those events (machines up and
stopped, replicas started and stopped, templates asleep and awake, snapshots, admissions refused), the
last 200 first; --json for programs. The API is /v1/events (server-sent
events, ?after=SEQ to resume) and /v1/stats.
snowglobe top snowglobe events --json | jq 'select(.kind == "admission.refused")'
Fleets: snowglobe apply
A snowglobe.toml says what the daemon should have, and snowglobe apply
gets it there: templates (a built site, a URL, or a Dockerfile directory built first), snapshots (a
machine of a template set up by commands, saved once a readiness command succeeds), templates of those
snapshots, services and machines. snowglobe diff says what apply would do;
snowglobe down removes all of it. An entry that changed is replaced, with what is made
from it; one gone from the file is removed; names something else uses are refused. One file goes from
source to running services (examples/fleet/snowglobe.toml):
[templates.py] build = "../python" # built first, for running natively network = "fetch" [snapshots.serving] template = "py" run = ["cd /srv && python3 -m http.server 8000 >/dev/null 2>&1 &"] ready = "wget -q -O /dev/null http://127.0.0.1:8000/" [templates.serving] snapshot = "serving" [services.web] template = "serving" ports = ["8080:8000"] min = 0 # scaled to zero, woken by a request idle = "5m" [machines.scratch] template = "py" ttl = "24h"
Snapshots of running machines
snowglobe snapshot save saves a machine whole: its memory, its running processes and the
files it wrote. Machines start from it again with up --snapshot, or it becomes a template
of its own with template add --snapshot. Set a machine up once (dependencies installed, a
server started, data loaded), then fork as many as you want, already set up.
snowglobe up py --name base snowglobe sh base 'pip install httpx && python3 -m http.server 8000 &' snowglobe snapshot save base --name ready # tens of milliseconds snowglobe template add --snapshot ready --name py-ready snowglobe up py-ready -p 8000 # installed, its server already listening snowglobe snapshot ls snowglobe snapshot rm ready
Services and scale to zero
A service puts a template's machines behind host ports that the daemon holds. Each connection (or
snowglobe sh <service>) goes to one of its replicas, started on demand.
With --min 0 it scales to zero: replicas with no connection or job for --idle
stop, and the next request forks one again. Made of a snapshot whose server was already listening, a
service at zero answers its first request in about 20 ms. --state keep saves the replica
before it stops, and the next one carries on from that save (one replica); --state
discard, the default, starts every replica from the template.
snowglobe service add web --template py-ready -p 8080:8000 --min 0 --max 3 --idle 5m --concurrency 10
snowglobe service add db --template pg --state keep --idle 10m
snowglobe service ls # replicas running now, of each
snowglobe service rm web
A replica takes --concurrency connections before another is started, up to
--max; under --min, replicas that stop are replaced. The API is
/v1/services and /v1/services/{name}/jobs.
MCP
snowglobe mcp <site> serves a site to an agent as an MCP server over stdio: an isolated
Linux machine with run_command, write_file and read_file tools. It
starts a native instance and stops it when the agent goes (--keep leaves it).
claude mcp add sandbox -- snowglobe mcp https://filipecabaco.github.io/snowglobe/python/
Limits, stopping and logs
| Flag | What it does | Exit |
|---|---|---|
--max-runtime 30m | ends the run after this long, whatever it is doing | 124 |
--stop-file PATH | ends the run once PATH exists: create it from anywhere | 143 |
--log-dir DIR | durable console.log, serial.log and events.jsonl |
Either way the job running in the guest is killed first, and a stop event saying why goes to
the log. Logs are appended as things happen, so they survive a crash of the run, and rotate at 8 MiB. A
named native instance logs to snowglobe/logs/<name> in your cache directory unless
--log-dir says otherwise.
console.log: the app's terminal, byte for byte.serial.log: the guest's serial line, with the kernel's own messages (an OOM kill, a panic) and the job shell.events.jsonl: one JSON object a line, each synced to disk:start,restored,job_start(the command, its environment variables' names but never their values, the timeout),job_end(status,timed_out,killed, milliseconds, the first 64 KiB of output) andstop(itsreason:stop-file,max-runtime,guest-stoppedorend, and the exit status).
Kills reach everything a job started. A job runs in a session of its own in the guest;
when it runs out of time or the instance stops, it gets TERM, then KILL after 3 s, including whatever it
put in the background. A process that makes a session of its own (a daemon that setsids) is
out of reach, as it would be anywhere.
A job's result is its own word. A job's exit status comes back on a line marked with a
one-time value the host types only to the job's launcher, so a job that merely prints a fake result isn't
believed. A job runs as root in the guest, though: one determined to can still read that value from the
launcher's memory and forge its own status, output and out/ files. That reaches only its own
result. The host still enforces the time limit, and if a job leaves the guest's shell busy, later jobs
fail with "the guest's shell is not answering" rather than run inside it.
Isolation
The first wall is the same everywhere: the guest is an emulated machine whose only ways out are its terminal, its files and, if the site has one, the network relay. Guest code never runs as host code.
| Browser | Native | Docker | |
|---|---|---|---|
| Around the emulator | the browser's renderer sandbox | no_new_privs, Landlock and seccomp on Linux; Seatbelt on macOS | read-only root, no capabilities, memory and pid limits |
| Host files | none | reads the site; writes its cache, socket, logs and a job's workspace | the site, read-only; logs if given |
| New processes | none possible | refused | inside the container only |
| Network | the page's fetch() and WebSocket | out the way your machine goes; private addresses off | the same, from the container |
| Needs | a browser | nothing (Linux or macOS) | Docker |
The native sandbox is strongest on Linux with Landlock (kernel 5.13 or later). A run says which parts it
got; SNOWGLOBE_SANDBOX=strict refuses to run without all of them, and
snowglobe-vm sandbox-check tries what a run must not be able to do and reports each.
Native runs of one site share its snapshot's memory: the first run writes it to the cache as a
read-only image (in ram/, which only you can open), and every run maps it copy-on-write. A page
a machine writes becomes its own, so one machine never sees another's memory, and each restore gets fresh
randomness (the guest kernel reseeds on it). The image is named by a hash of the whole snapshot and checked
before use (yours, read-only, one link); anything off and the run restores the usual way.
SNOWGLOBE_RAM_IMAGE=off always does.
What none of them do: hide a site's contents from whoever runs it (the snapshot is theirs to read), or make network access safe beyond the rules above. Give a guest a network only when the work needs it.
Saving your work
A run in the browser saves the whole machine locally: memory, processes and the files it wrote, plus the terminal. Nothing is sent to a server.
- Browser, automatic is the default: every minute while the page is visible (or every 5 or 15 minutes, or only on Save now).
- Backup file downloads a
.snowglobefile you can restore later. Backups can hold passwords, tokens and database contents and are not encrypted: keep them private, and restore only files you trust. - Folder, automatic saves into a folder you pick, on browsers with
showDirectoryPicker()(desktop Chrome and Edge).
A refresh resumes the last completed save, not necessarily your latest changes, so use Save now before leaving important work. Saves are tied to the exact site build: after an incompatible rebuild, older ones are kept for export rather than restored.
How it works
- Boot layer. snowglobe adds a kernel, an initramfs that mounts the root filesystem
over 9p, and BusyBox's
initandgettywith a terminal running your command, as files on top of your image's layers. Nothing runs in the image. - Filesystem. The flattened layers become a 9p filesystem: a JSON tree and one compressed blob per unique file, fetched on demand. Files of 2 MiB and more are split into content-defined chunks, so a read fetches only what it covers.
- Snapshot. The guest boots headless until the app is ready, and the whole machine is
saved. Visitors,
snowglobe run, bundles andsnowglobe mcpall restore it. - Machines. Natively, the snapshot's memory becomes an image the host maps
copy-on-write into every instance, and a template restores once and forks a machine per command, per
snowglobe upor per service replica, each sharing what it doesn't write. - Warm pack. The files read while booting and exercising are bundled into one download the page fetches in the background.
When a build goes wrong
- "the boot stalled". Nothing was printed, fetched or computed for 5 minutes before the
app was ready. The build prints every CPU's state and boots once more; twice usually means the app
waits for something that never comes, such as
--readytext it never prints.SNOWGLOBE_BOOT_STALL_SECSchanges the limit, and0turns it off. - "not arm64". Build
FROM alpineorarm64v8/alpine; on an x86 host, install QEMU's binfmt handlers first. - A request answers 502. In a page, the server doesn't allow CORS. Under
snowglobe runthe same request goes out from your machine instead. - A service doesn't start. There is no OpenRC: start it from the image's command.
CLI reference
snowglobe help prints all of this.
Commands
| Command | What it does |
|---|---|
| build <dir | image> | build a site from a Dockerfile directory, an image reference or an image archive |
| serve [dir] | preview a built site (--port, default 8000) |
| run [dir | url] | run a site in a terminal, in a container or natively |
| exec <name> <cmd> | type a command into a running instance and print what it printed |
| attach <name> | join a running instance (ctrl-] detaches) |
| ps · stop <name> | list running instances · throw one away |
| sh <name> '<cmd>' | run a command on a native instance's shell (--json, --timeout S) |
| cp <src> <dst> | copy files in or out of a native instance (name:/path on one side) |
| mcp <dir | url> | serve a site to an agent over MCP (--name, --keep, --log-dir) |
| bundle <dir | url> | make a site one executable (-o, --target linux|darwin, --runtime) |
| pack <dir> | pack a site into one tarball (-o site.tar.gz or .tar.zst) |
| pool <dir> | let every site under a directory share one blob store |
| pwa <dir> | make a site an installable app (--name, --short-name) |
| warm <dir> <reads.json…> | add recorded reads to a site's warm pack |
| daemon | the orchestrator, on a Unix socket (--listen ADDR --token-file F for TCP; --memory 8G, --wait 30s) |
| template add <site> · ls · rm | restore a site once for machines to fork from (--name, --snapshot, --idle, --max-machines) |
| up <template> | a machine forked from a template (--name, -p PORT, --ttl, --snapshot, --label K=V) |
| snapshot save <machine> · ls · rm | save a running machine whole, to start machines and templates from |
| service add <name> · ls · rm | a template's machines behind host ports, started on demand and scaled to zero (--template, -p, --min, --max, --idle, --concurrency, --state) |
| events · top | the daemon's events as they happen · the daemon live: memory, templates, services, machines |
| apply · diff · down | get the daemon to what a fleet file says · show the plan · remove the fleet (-f snowglobe.toml) |
| tools · version | build or find the tools image · print the version |
Build flags
| Flag | Label | What it sets |
|---|---|---|
| --out DIR | output directory (default dist) | |
| --cmd CMD | the program on the terminal (default: the image's entrypoint and command) | |
| --ready TEXT | snowglobe.ready | output that means the app is ready (default: 5 s of quiet) |
| --title TEXT | snowglobe.title | page title |
| --memory MB | snowglobe.memory | guest RAM (default 512) |
| --cpus N | snowglobe.cpus | guest CPUs, 1 to 8 (default 1) |
| --build-cpus N | snowglobe.build-cpus | CPUs to boot on before the snapshot (default: the larger of --cpus and 4) |
| --exercise CMD | snowglobe.exercise | a command whose reads are preloaded; repeatable (label: a JSON array) |
| --warm REGEX | snowglobe.warm | extra guest paths to preload |
| --network fetch | snowglobe.network | give the guest a network through the browser (default none) |
| --tunnel [HOST:]PORT=URL | snowglobe.tunnels | carry TCP to HOST:PORT over a WebSocket; repeatable |
| --tunnel-token URL | snowglobe.tunnel-token | where a fresh token comes from, for {token} in tunnel URLs |
| --allow-private | let the build reach loopback and private addresses | |
| --builder B | docker (default) or native | |
| --sandbox | snapshot for running natively (run, bundle) rather than in a page | |
| --no-chunks | store every file in one blob, however large |
Run flags
| Flag | What it does |
|---|---|
| --name N · --detach | name the instance · run it in the background |
| --runtime docker|native | in a container (default) or natively |
| --fork | every sh on a fresh copy of the machine (see --fork) |
| -p [IP:]HOST:GUEST | forward a host port to a guest port (needs --network fetch) |
| --record-reads FILE | write every file the guest reads, for warm |
| --tunnel · --allow-private | replace the site's tunnels · let the guest reach this machine |
| --max-runtime · --stop-file · --log-dir | see limits, stopping and logs |
Environment
| Variable | What it does |
|---|---|
| SNOWGLOBE_BUILDER | default for --builder |
| SNOWGLOBE_RUNTIME | default for --runtime |
| SNOWGLOBE_VM | the snowglobe-vm to use instead of the release's |
| SNOWGLOBE_BOOT | a kernel archive to use instead of the release's |
| SNOWGLOBE_TOOLS_BUILD=1 | always compile the tools image locally |
| SNOWGLOBE_SANDBOX | strict refuses to run without the whole native sandbox; off skips it |
| SNOWGLOBE_BOOT_STALL_SECS | how long a boot may do nothing before it counts as stalled (default 300; 0: no limit) |
| SNOWGLOBE_CACHE | where native runs keep what they learn and the snapshots' memory images (default ~/.cache/snowglobe) |
| SNOWGLOBE_RAM_IMAGE=off | restore into memory of the run's own instead of sharing the snapshot's image |
| ARMLESS_JIT=off | keep armless's interpreter instead of its native JIT |
| ARMLESS_JIT_PREWARM=off | don't compile what earlier runs recorded at start |
| HTTPS_PROXY · HTTP_PROXY · NO_PROXY · SSL_CERT_FILE | passed to snowglobe run's container |