# The sandbox environment Every sandbox is a Firecracker microVM with its own Linux kernel and disk, started from the same image. This page says what is in it, who you are inside it, and what it can reach. ## What is installed The image is Ubuntu 24.04.5 LTS (noble) for amd64, on a 6.1 kernel. | Kind | What | | ---------------- | -------------------------------------------------------------------------------------------- | | Python | Python 3.12 (`python3` and `python`), `pip` and `pip3`, `venv`, and uv 0.12.17 (`uv`, `uvx`) | | JavaScript | Node.js 24.21.0 (`node`, `npm`, `npx`); Bun 1.4.0 (`bun`) | | Build | `gcc`, `g++`, `make` (build-essential) | | Source and fetch | `git`, `curl`, `wget`, `ssh`, `zip`, `unzip`, `xz` | | Search and data | `rg` (ripgrep), `fd`, `jq`, `sqlite3` | | System | `sudo`, `chronyd` | Anything else installs the usual way: `sudo apt-get install -y ...`, `pip install ...`, `npm install ...`, `uv pip install ...`. To start every sandbox with your dependencies already there, build a [custom image](./javascript#custom-images). Java, Go and Rust are not installed. Install Ubuntu's packages with `sudo apt-get install`, or a newer release with the language's own installer, such as `rustup` for Rust. To have one in every sandbox, build a custom image whose recipe installs it, or start the image from a public image that already has it. Docker is not installed either, and running containers inside a sandbox has not been tested. To start sandboxes from a Dockerfile, build it as a custom image instead; single-stage Dockerfiles are supported. Browsers are not in the image, because every byte of it counts against each sandbox's disk. For Playwright's Chromium: ```bash no-run sudo npx playwright install-deps chromium npx playwright install chromium ``` ## Who you are - Commands run as the user `runtime` (uid 1000), with `bash`. - `HOME` is `/workspace`, which is also the default working directory. Files there belong to you. - `sudo` works without a password: you are root inside your own sandbox. Root cannot change what the sandbox may reach, how much CPU and memory it has, or what it costs; those are enforced on the host, outside the sandbox. - `PATH` starts with `/workspace/.local/bin`, then `/usr/local/bin`, `/usr/bin` and `/bin`. `LANG` is `C.UTF-8`. `pip install` works without a virtual environment. As `runtime` it installs to `/workspace/.local` (whose `bin` is first on `PATH`); under `sudo` it installs to `/usr/local`. The image's `/etc/pip.conf` sets `break-system-packages`, so the Ubuntu rule against installing into the system Python (PEP 668) does not stop you. Use `python3 -m venv` or `uv` when you want isolation. ## Disk, CPU and memory The disk you ask for with `diskMiB` includes the system image, which uses about 1 GiB of it, so the default 4 GiB sandbox has about 3 GiB free; ask for more when you install a lot. The smallest disk is 3072 MiB, the size of the image file. Each sandbox's disk reads and writes are limited in speed, to about 40 MB/s and 2,000 operations a second each way whatever the sandbox's size, so one sandbox cannot swamp the drive it shares with others. CPU is shared by default, with a guaranteed floor (`cpuFloorMillis`, 50 thousandths of a vCPU unless you ask for more) and bursts up to `vcpu` cores. Memory is what you ask for. See [pricing](./pricing) for what each costs. ## The network A sandbox has no network card. Everything outbound goes through a proxy on the host, and the image sets the environment every tool needs to find it: ```text HTTP_PROXY=http://127.0.0.1:10800 http_proxy=http://127.0.0.1:10800 HTTPS_PROXY=http://127.0.0.1:10800 https_proxy=http://127.0.0.1:10800 NO_PROXY=localhost,127.0.0.1,::1 NODE_USE_ENV_PROXY=1 ``` `sudo` keeps these. A program that ignores proxy settings and opens raw sockets does not reach the internet. By default a sandbox reaches the public web on ports 443 and 80. Private and internal addresses are refused. Mail ports (25, 465 and 587) are closed unless support enables them for your account, and a few ports are never reachable: telnet, Windows RPC, NetBIOS and SMB, and IRC. Each sandbox has its own rules, set at create (`network`) or at any time after, applied at once, to open connections too: ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ network: { internet: true, allow: ["pypi.org", "*.pythonhosted.org"] }, }); await sbx.network.set({ internet: true, deny: ["example.com"] }); await sbx.network.set({ internet: false }); console.log(await sbx.network.get()); ``` - `internet: false` refuses every outbound connection. - `allow` narrows the web to a list: domains, `*.domain` for every name under one, addresses or CIDR ranges. - `deny` always wins. - `connect` opens `host:port` pairs beyond the web ports, such as your own database or `github.com:22`, for paid sandboxes of accounts that have made a purchase. Inside the sandbox, `python3 /usr/local/lib/runtime/guest-egress.py forward 5432 db.example.com:5432` gives a program a local port to use. The rules apply to root inside the sandbox too. Nothing can connect in to a sandbox except through a preview: an HTTPS address for one port that you share on purpose, private with a token by default. See [JavaScript](./javascript#share-a-port) or `npx withruntime sandbox preview`. A sandbox with no preview accepts no incoming connections. ## Time and lifetime The clock is kept on the host's clock, and set again after every wake. A sandbox runs until its lease ends (`timeoutSeconds`, at most an hour ahead, which `extend` moves), then pauses or stops as `onLeaseEnd` says. A host-side lease bounds execution even if management is unavailable. A stopped sandbox is not a backup; copy out what you need to keep.