start
CLI
The CLI drives sandboxes. It is the way most people should use them — the SDK is the programmatic alternative, not the default. macOS and Linux (Intel and ARM); Windows under WSL.
install & auth
Paste an API key from your dashboard. It is verified before it is saved, so a bad paste fails here rather than on some later command.
curl -fsSL https://boltzlabs.john221wick.in/cli/install.sh | sh
bzlabs auth # paste a key, or see who you are
export BOLTZLABS_API_KEY=… # or skip the login entirely, in CIsandboxes
With no arguments, create asks two questions. Machine comes
first because it sets the price and the memory ceiling — a 256 MB nano is a poor home for pytorch.
$ bzlabs create
Machine (↑/↓ to move, enter to select)
nano 1 vCPU · 256 MB RAM · 10 GB disk · $0.03*/hr
❯ small 2 vCPU · 512 MB RAM · 10 GB disk · $0.06*/hr
medium 2 vCPU · 1024 MB RAM · 20 GB disk · $0.11*/hr
large 4 vCPU · 4096 MB RAM · 40 GB disk · $0.24*/hr
Environment (↑/↓ to move, enter to select)
base debian + bash, curl, git
❯ python python 3 + numpy, pandas, requests
node node 22
pytorch python + torch (CPU)
opencode OpenCode
claude-code Claude Code
codex OpenAI Codex CLI
deepagents Deep Agents Code
aider Aider
Internet (↑/↓ to move, enter to select)
❯ off no outbound network (default)
on allow outbound internet
Name (blank for auto): scratch
Idle timeout seconds (0 = platform default) [600]:
Max lifetime seconds (0 = platform default) [3600]:
created sb-eb02cc (python, small) — $0.06*/hr
connect with: bzlabs connect scratch* Sandbox prices have a one-hour minimum. Runtime after the first 60 minutes is billed per second.
Every prompt has a default — press enter all the way through for small / base / internet off /
10 min idle / 1 h max. Each prompt is skipped when its flag is given, so the same command runs
unattended. Pass --internet when the box needs the
network. Coding-agent environments default internet access on for authentication and model APIs;
pass --no-internet to isolate one. A sandbox bills for as
long as it exists, so cost is a column rather than a separate command — and rm is what ends the bill, not disconnecting. Names must be
unique among your running sandboxes.
bzlabs create --machine small --environment python --name scratch
bzlabs create --internet --idle-timeout 600 --max-lifetime 3600
bzlabs ls # id, name, status, runtime, cost
bzlabs status scratch # id or name
bzlabs rm scratch # stops the meter
bzlabs machines # nano · small · medium · large, with prices
bzlabs environments # runtimes plus OpenCode, Claude, Codex, Deep Agents, AiderNaming neither is fine: the machine defaults to small and
the environment to base.
driving one
exec runs one command and comes back; connect hands over your keyboard as a real PTY.
Both take the sandbox id or the name you gave it. Everything
after the ref is joined back into one command line, so quote anything your local shell would otherwise eat — quotes, pipes
and redirects never reach the sandbox unless you do.
bzlabs exec scratch pip install requests
bzlabs exec scratch "cd /app && pytest -q"
bzlabs connect scratch # Ctrl-D to leaveThe sandbox's exit code becomes the CLI's, so && and || work as usual. There is no file-copy command, and exec does not forward local stdin. Clone inputs from the network
or embed a small encoded payload in the command. Captured stdout can be redirected locally:
payload=$(base64 < ./train.py | tr -d '\n')
bzlabs exec scratch "printf %s '$payload' | base64 -d > /workspace/train.py"
bzlabs exec scratch cat /workspace/results.json > ./out.jsonports
Run a server inside the sandbox, then reach it from your machine. Create with --internet when you need this.
bzlabs create --name web --internet --environment python
bzlabs exec web "python -m http.server 8000" &
bzlabs url web 8000 # link you can open or curl
bzlabs open web 8000 # open it in a browser
bzlabs forward web 8000 # localhost:8000 → sandbox:8000
bzlabs forward web 9000:8000 # localhost:9000 → sandbox:8000
# Ctrl-C stops the forwardthe other two
every command
| command | does | aliases / flags |
|---|---|---|
| bzlabs auth | log in, or show who you are | login · logout · status |
| bzlabs create | create a sandbox | --machine --environment --name |
| bzlabs ls | list your sandboxes | list |
| bzlabs status <id|name> | show one sandbox in full | — |
| bzlabs connect <id|name> | open an interactive shell | shell |
| bzlabs exec <id|name> <cmd…> | run one command, print the output | — |
| bzlabs run <file> | execute code — no sandbox involved | --language (required) |
| bzlabs rm <id|name> | destroy a sandbox | delete · destroy |
| bzlabs url <id|name> <port> | URL for a port inside the sandbox | — |
| bzlabs open <id|name> <port> | open that URL in a browser | — |
| bzlabs forward <id|name> [local:]remote | forward a local port into the sandbox | port-forward |
| bzlabs environments | what a sandbox can ship with | — |
| bzlabs machines | machines and prices | — |
| bzlabs languages | language codes run accepts | — |
| bzlabs update | install a newer CLI | — |
| bzlabs version | print the installed build | -v |
| bzlabs help | the same list, in your terminal | -h |
the same thing in python
sb is a Sandbox; the module-level calls use a default
client built from your key. Same two words on both sides.
| cli | python |
|---|---|
| bzlabs create | Sandbox() |
| bzlabs exec <id|name> <cmd> | sb.exec("cmd") |
| bzlabs connect <id|name> | sb.terminal() |
| bzlabs rm <id|name> | sb.delete() |
| bzlabs ls | boltzlabs.sandboxes() |
| bzlabs status <id|name> | boltzlabs.sandbox("scratch") |
| bzlabs url <id|name> <port> | sb.url(port) |
| bzlabs environments | boltzlabs.environments() |
| bzlabs machines | boltzlabs.machines() |
| bzlabs auth status | boltzlabs.me() |
| bzlabs run --language … | boltzlabs.execute(…, language=…) |
| bzlabs languages | boltzlabs.languages() |
| bzlabs update | pip install -U boltzlabs |