How to build a custom sandbox image from a list of packages
Call runtime.images.build({ name, recipe: { pip, apt, npm } }) once, then create each sandbox with image: name; the build is free.
On Runtime building an image costs nothing and uses no trial hours, and the
free trial stores your first three images free for as long as you keep them.
After that a stored image costs $0.08 per decimal GB per 30-day month on its
whole file (rate in force since 23 September 2026,
pricing). Every sandbox started
from it has the packages already installed, instead of spending its first
minute on pip install.
Build it and start from it
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();const image = await runtime.images.build( { name: "data", recipe: { pip: ["pandas"], apt: ["jq"] } }, { onLog: (line) => console.log(line.text) },);await using sbx = await runtime.sandboxes.create({ image: "data" });console.log( image.version, (await sbx.exec("python3 -c 'import pandas; print(pandas.__version__)'")).stdout,);Pythonfrom withruntime import Runtimeruntime = Runtime()image = runtime.images.build(name="data", recipe={"pip": ["pandas"], "apt": ["jq"]}, on_log=lambda line: print(line["text"]))with runtime.sandboxes.create(image=image["id"]) as sbx: print(sbx.exec("python3 -c 'import pandas; print(pandas.__version__)'").stdout)Terminalimg=$(runtime image build --pip pandas --apt jq --name data)runtime sandbox create --image "${img}"build streams the log, waits until the image is ready, and throws with the
build's own error if a step fails. images.create queues the same build and
returns at once, for a job that should not wait.
What a recipe takes
| Field | What it does |
|---|---|
base |
runtime by default, Runtime's image with Python, Node and Bun; or any image |
apt |
Ubuntu packages |
pip |
Python packages |
npm |
Node packages |
commands |
Shell commands to run in the build |
files |
Small files written into the image, 256 files and 1 MiB in all |
env |
Environment variables set in the image |
workdir |
The working directory |
The CLI covers the common case with --pip, --apt and --npm; for
commands, files or env, use an SDK or a
Dockerfile.
Make it start a server too
An image can say what runs when a sandbox starts from it and when that sandbox counts as ready. The create then answers only once the server listens.
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "api", recipe: { pip: ["fastapi", "uvicorn"] }, start: { command: "uvicorn main:app --port 8000", readyPort: 8000, readyTimeoutSeconds: 60 },});const sbx = await runtime.sandboxes.create({ image: "api" });console.log(sbx.info.start); // { state: "ready", readyMs: ... }start.state is ready, started (no ready check), timeout (the check did
not pass within readyTimeoutSeconds, 60 by default, at most 300) or exited
(the command ended first) (start and ready commands).
Recipes worth baking
- R, Java or Go for the code interpreter. Otherwise each sandbox installs
them the first time it uses them: about 35 seconds for Java or Go and 90 for
R, measured on 23 September 2026. Put them in
aptto skip that. - Docker.
commands: ["enable-docker --no-start"]gives every sandbox Docker Engine, Buildx and Compose, started on the firstdockercommand (Docker in a sandbox). - Your agent's tools. A coding agent's CLI, linters and test runners, so each task starts working at once (coding agent sandbox).
Limits
| Limit | Value |
|---|---|
| Builds at once | 1 until the account has bought credit, then 4 |
| Build machine | 1 to 8 vCPUs (2 by default), 1 to 16 GiB of memory (4 GiB) |
| Scratch disk | 1 to 32 GiB (4 GiB by default) |
| Build time | 60 seconds to 1 hour (30 minutes by default) |
| Image size | 512 MiB to 20 GiB (8 GiB by default) |
The build runs in its own Firecracker virtual machine, with the same web access as a sandbox (limits).
Versions, tags and rebuilds
Each build of a name is its next version and takes the tag latest, or the
tags you give. image on a create takes an id, name, name:tag or
name@version, so production can pin data@3 while tests follow data.
A rebuild whose first steps match an earlier build of your account starts from
the latest matching checkpoint, and an identical build is copied at once
instead of built. Pass cache: false to build every step from scratch
(faster rebuilds).
Mistakes to avoid
- Installing packages in every sandbox. Each fresh sandbox then pays for the download and install in running time. Build once.
- Passing both
imageandsnapshotto a create. A sandbox starts from one or the other. A snapshot keeps a whole machine, memory included; an image keeps files. - Giving a recipe and a Dockerfile together. A build takes exactly one
source: a
recipe, animageor adockerfile. - Forgetting old versions. Each stored image is charged; remove versions you
no longer need with
runtime image rm data@1.
Facts on this page were checked on 25 September 2026.