Skip to main content
A sandbox runs one image, chosen by runtime or named outright with image. Five runtimes come built in. node and python are the stock Docker Hub images. node-python, media and browser are built from images/ in the nativesandbox repository for linux/amd64 and linux/arm64, and tagged with the package version, so nativesandbox@0.4.0 uses nativesandbox-node-python:0.4.0. Sizes are uncompressed, on disk.

node-python

Node and Python in one image. node has no Python and python has no npm, so a job that edits with a Python script and builds with npm needs two sandboxes on the stock images — and since the image is part of a sandbox’s identity, switching between them replaces the sandbox and loses whatever it had installed. node-python keeps it to one.
  • Node 22 and npm, Python 3 (Alpine’s, 3.14 at the time of writing) with pip, git and curl.
  • python is linked to python3.
  • PIP_BREAK_SYSTEM_PACKAGES=1: a sandbox is disposable, so pip install goes into the system Python rather than being refused.

media

Everything in node-python, plus ffmpeg and libvips, for video and images.

browser

Everything in node-python, plus headless Chromium driven by Playwright, and axe for accessibility. Only the headless build is installed — the one chromium.launch() uses; full Chromium is for headed runs, which a sandbox has no use for. The packages are installed globally, so a script can import or require them without an npm install — which would otherwise download a browser on every run. require() finds them through NODE_PATH; ESM import ignores NODE_PATH, so a small resolve hook, loaded through NODE_OPTIONS, retries a bare import from the global directory once normal resolution has failed. A project’s own node_modules always wins, and npm install behaves exactly as anywhere else.
Why Debian. Playwright’s Chromium is built against glibc, and Playwright does not support Alpine’s musl. An Alpine image with Alpine’s own Chromium package is smaller, but it is unsupported, and the browser and Playwright then change versions independently. Memory and /dev/shm. Chromium needs about a gigabyte; give the sandbox MiB(1024) or more. It also keeps renderer state in /dev/shm, and an engine’s default 64 MB crashes it on an ordinary page, so the browser runtime gets 512 MiB by default — see RUNTIME_DEFAULTS and the shmSize option in Resource limits. No capabilities. Under the default hardening every capability is dropped, so Chromium’s own sandbox cannot start. Playwright already launches Chromium without it (chromiumSandbox: false); a tool that launches CHROME_PATH itself must pass --no-sandbox. The container is the sandbox.

Pulling

An image is pulled the first time a runtime is asked for. That is fine for the Alpine images and wrong for browser: a first command that waits for several hundred megabytes blows any reasonable timeout. Pull ahead, at deploy or at boot:
With no runtimes, both pull every image the instance knows. A name that is not a runtime is pulled as an image reference.

Versions

The built images follow the package version. A sandbox’s image is part of its identity — create() never reuses a sandbox across images — so upgrading nativesandbox hands out fresh sandboxes on the new images rather than mixing versions.

Bring your own image

Name an image for one sandbox, or change the mapping for every one:
An image needs two things to run as a sandbox: a POSIX sh, which runs the self-stop watchdog and every command, and a writable /workspace, where the workspace is mounted. Anything else is up to you.