Skip to main content
A sandbox is a container with a workspace directory mounted into it. It is identified by a name you choose, and that name is how you find it again — across calls, and across process restarts.

Reuse is meet-or-exceed, not equality

Calling create() for a name that already exists returns the existing sandbox when its shape still serves the request, starting it first if it had stopped. The rule is meet-or-exceed, and the distinction is load-bearing. Under equality, a build asking for 2 GiB and a small command asking for 256 MiB never share a sandbox, so each one replaces the other’s and destroys whatever it had installed. That turns every dependency install cold. A bigger sandbox runs a smaller command perfectly well; the reverse is not true, so a raised budget still takes effect.
The image is an exception, because it is an identity rather than a quantity: a Python command can never land in a Node sandbox, and no amount of spare memory makes it able to.

A rebuild keeps the workspace

When a sandbox no longer serves — a different image, more room, a tighter ceiling — the container is rebuilt and the workspace stays. A job that moves between images keeps what it installed:
Installed packages with native parts are built for the image that installed them. browser is Debian (glibc) and the others are Alpine (musl), so run installs and builds in the image that will use them, and give the browser step what is already built.

Ceilings run the other way

Capacities meet-or-exceed. Ceilings meet-or-undercut. A sandbox entitled to live for an hour is not handed to a caller who just asked for a minute, so it is rebuilt. The reverse is fine: one that stops sooner than asked is harmless, because the next create() starts it again.
Reuse also refuses a sandbox already older than the requested ceiling. Being entitled to live long enough is not the same as having done so — a sweep may not have run yet, and handing back a sandbox past its ceiling would break it on arrival.

Forcing a fresh one

When the workspace itself is suspect, replace skips reuse entirely:
It removes the container and its workspace, then builds clean. replace is an action, not a property. It is never recorded on the sandbox and never influences whether a later create() reuses it — otherwise every subsequent call would keep replacing. You should rarely need it: a sandbox whose shape no longer serves is already rebuilt without it.

Finding what exists

list() is scoped by label to the sandboxes this runtime created, so a sweep can never touch a container it does not own. Set prefix on the constructor to separate one fleet from another on a shared host:

Removing

stop() is the reversible one: the next create() starts the same container again, with everything the last command installed still in place. remove() is not reversible — it deletes the workspace directory. Removing something that is already gone is not an error; remove() returns whether there was anything there.