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'