Templates
A template is what a sandbox boots from: the operating system, the installed tools, and the size. Impello ships a set of defaults, and you pick one by name when you create a sandbox. This page lists the defaults, what each one holds, and how to reach the CLIs and services inside them.
Choose a template
Pass the name to Sandbox.create. With no name you get base.
import { Sandbox } from '@impello/sdk'
const sandbox = await Sandbox.create('codex')
const result = await sandbox.commands.run('codex --version')
console.log(result.stdout)
await sandbox.kill()Both SDKs read your key from IMPELLO_API_KEY; see
API keys. In TypeScript the name can also go in the options
object as template, and in Python as the template keyword argument.
The default templates
Every default ends as the user user with the working directory
/home/user, so a command you run lands there. codex, opencode, pi,
amp, droid, grok, openclaw, k3s and browser start from base. claude,
code-interpreter, desktop and omarchy are built from their own image, so
they do not carry base's toolchain. The table lists each default with its
size.
| Name | What it holds | vCPU | Memory |
|---|---|---|---|
base | Debian 12 with Python 3.12, Node 22, git, GitHub CLI, jq, ripgrep, curl, unzip, yarn, pnpm and build tools. The default. | 2 | 1 GB |
claude | Claude Code on Ubuntu 25.04, with Docker enabled at boot, Node 22, Python 3, pipx and uv. Binary at ~/.local/bin/claude. | 2 | 2 GB |
codex | base plus the Codex CLI, installed globally with npm. | 2 | 2 GB |
opencode | base plus the OpenCode CLI, installed globally with npm. | 2 | 2 GB |
pi | base plus the pi coding agent, installed globally with npm. | 2 | 2 GB |
amp | base plus the Amp CLI at ~/.local/bin/amp. | 2 | 2 GB |
droid | base plus the Droid CLI at ~/.local/bin/droid. | 2 | 2 GB |
grok | base plus the Grok CLI at ~/.grok/bin/grok. | 2 | 2 GB |
openclaw | base plus the OpenClaw CLI at ~/.npm-global/bin/openclaw. | 2 | 4 GB |
code-interpreter | Python 3.13 with Jupyter kernels for Python, R, JavaScript, Bash and Java. Jupyter starts with the sandbox. | 2 | 2 GB |
k3s | base plus single-node Kubernetes (k3s) and kubectl. The cluster starts at boot and KUBECONFIG is set. | 4 | 8 GB |
desktop | Ubuntu 22.04 with XFCE, Firefox, Chrome, VS Code, LibreOffice, x11vnc and noVNC. The GUI stack is installed but no start command is declared, so a sandbox from this template boots to a shell only. There is no supported way to start the desktop; use omarchy for a browser-reachable desktop. | 4 | 8 GB |
omarchy | Arch Linux with the Omarchy shell on a sway session, served over noVNC on port 6080. | 4 | 8 GB |
browser | base plus a headful Chromium (fingerprint-chromium 150) with a fingerprint per profile, CDP on port 9222 and noVNC on port 6080. See Browser. | 2 | 4 GB |
The CLIs installed with npm (codex, opencode, pi) are on the PATH. The
vendor installers put their binary under the home directory at the path shown,
and the template appends that directory to ~/.bashrc. commands.run runs
your command with bash -l -c. A login shell reads ~/.profile, not the end
of ~/.bashrc, so that appended line does not apply — call the binary by its
full path.
Aliases
Two templates answer to a second name. claude is also claude-code, and
code-interpreter is also code-interpreter-v1. Either name creates the same
sandbox.
Run an agent CLI
The base-derived agent templates (codex, opencode, pi, amp, droid,
grok, openclaw) are base plus one CLI; claude is its own Ubuntu image
with Docker, uv and pipx. In every one, nothing is signed in — pass the
vendor's key with envs when you create the sandbox. Those variables are set
for every command you run, and envs on a single command overrides them.
The example runs one prompt through Claude Code.
import { Sandbox } from '@impello/sdk'
const key = process.env.ANTHROPIC_API_KEY
if (!key) throw new Error('Set ANTHROPIC_API_KEY')
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: key },
timeoutMs: 600_000,
})
const result = await sandbox.commands.run(
'~/.local/bin/claude -p "List the files in this directory"',
{ timeoutMs: 300_000 }
)
console.log(result.stdout)
await sandbox.kill()The pattern is the same for all of them: create by name, pass the vendor's key
in envs, run the binary. -p is Claude Code's print mode, which runs one
prompt and writes the answer to stdout; each vendor's CLI has its own flags, so
check the vendor's documentation. A command gives up after 60 seconds unless
you raise timeoutMs (timeout, in seconds, in Python); see
Run commands.
Reach a service in a template
Three templates declare a start command and wait for it before the sandbox is
ready: omarchy (noVNC on 6080), browser (CDP on 9222 and noVNC on 6080;
see Browser) and code-interpreter (an HTTP server on
49999). k3s starts its cluster from a systemd unit instead, shortly after
boot. getHost(port) returns the host for a port; you supply the scheme and
the path.
The omarchy desktop is served at the path /vnc.html on port 6080. The
example prints its URL and leaves the sandbox running for 30 minutes so you can
open it.
import { Sandbox } from '@impello/sdk'
const sandbox = await Sandbox.create('omarchy', {
timeoutMs: 1_800_000,
})
console.log(`https://${sandbox.getHost(6080)}/vnc.html`)code-interpreter starts Jupyter with the sandbox and an HTTP server on port
49999 whose health check is /health. Neither SDK ships a client for that
server, so run code in this template with commands.run, for example
python3 -c "print(1 + 1)".
Sizes
Each template declares the vCPU count and memory it is built with, listed in
the table above. Your plan sets the largest sandbox you can run: the free
trial's ceiling is 4 vCPU and 8 GB per sandbox, and every paid plan's is 8 vCPU
and 16 GB. Every default template fits inside all of them, the trial included:
k3s, desktop and omarchy are the largest, at 4 vCPU and 8 GB. See
Limits and pricing.
Updates
The defaults are rebuilt every week. The agent CLIs are installed unpinned, so
each rebuild picks up the vendor's current release and the version inside a
fresh sandbox moves with it. To freeze a version, build your own template from
a default with fromTemplate('claude') and create sandboxes from that name
instead.
Check that a name exists
Template.exists returns true when a name is taken, including a default
template and a name another team owns. It lives in the
@impello/sdk/template subpath and is TypeScript only; the Python SDK has no
template module.
import { Template } from '@impello/sdk/template'
const exists = await Template.exists('my-app')
console.log(exists)
Build your own
When no default fits, start from one of them or from any Docker image and add what you need. The builder is TypeScript only, but the template it produces can be used from either SDK by name.
Next: Build a custom template.