Skip to main content
Before working through the tutorials, it helps to understand a few core concepts — starting with the most common question.

Box types

Why are there different box types?

Every box is the same thing under the hood: a lightweight microVM (a real virtual machine that boots in under a second). The different box types exist for convenience — they bundle a default image and add helper methods so you don’t have to wire things up yourself. Some extend SimpleBox directly (CodeBox, InteractiveBox), while others are standalone wrappers with their own specialized APIs (BrowserBox, ComputerBox). Here’s what each box type actually is:
Box typeWhat it addsDefault imageImage changeable?Has exec()?
SimpleBoxShell commands(you choose)YesYes
CodeBoxrun(), install_package()python:slimYes (needs Python)Yes
BrowserBoxconnect(), playwright_endpoint()Playwright (fixed)NoNo
ComputerBoxscreenshot(), mouse/keyboardwebtop (fixed)NoNo
InteractiveBoxPTY terminal session(you choose)YesYes
SimpleBox — the base box type.
  • You pick any image, you run shell commands with exec().
  • Image: required in Python, defaults to python:slim in Node.js.
CodeBox — extends SimpleBox with Python shortcuts.
  • Defaults to the python:slim image (which already has Python inside). Does not install Python — the image already has it.
  • Adds run() (calls python -c for you), install_package() (calls pip install for you), run_script().
  • Since CodeBox extends SimpleBox, you still have exec() too — CodeBox can do everything SimpleBox can.
  • Image: changeable, but must have Python inside (e.g. python:3.11). Without Python, run() will fail.
BrowserBox — a standalone box type for browser automation.
  • Uses the Playwright image (mcr.microsoft.com/playwright:v1.58.0-jammy) which already has Chromium, Firefox, and WebKit inside. Does not install a browser — the image already has them.
  • Provides playwright_endpoint(), endpoint(), connect() to get browser connection URLs.
  • Image: fixed, cannot be changed.
ComputerBox — a standalone box type for desktop automation.
  • Uses the webtop image (lscr.io/linuxserver/webtop:ubuntu-xfce) which already has a GUI desktop inside.
  • Provides screenshot(), mouse_move(), left_click(), type(), key() for GUI automation.
  • Image: fixed, cannot be changed.
InteractiveBox — extends SimpleBox with a different connection mode.
  • Does not add anything to the image. Instead, it changes how the SDK connects: it attaches your terminal directly to the VM’s shell, like SSH. Every Linux image already has a shell.
  • Adds wait() to block until the shell session ends.
  • Since InteractiveBox extends SimpleBox, you still have exec() too.
  • Image: required (any OCI image works).

So which one should I use?

I need to…UseWhy
Run Python code from an AI agentCodeBoxrun() and install_package() handle Python for you
Run shell commands, scripts, or non-Python toolsSimpleBoxPick any image, use exec() to run anything
Control a web browserBrowserBoxGives you a ready-to-use Playwright/Puppeteer endpoint
Interact with a desktop GUIComputerBoxGives you screenshot, mouse, and keyboard control
Use an interactive terminal sessionInteractiveBoxAttaches your terminal to the VM like SSH
Still not sure? Start with CodeBox. It handles the most common AI use case (running untrusted Python) and you can always switch later.

Could I just use SimpleBox for everything?

For CodeBox and InteractiveBox, yes — they extend SimpleBox and you could replicate what they do manually:
# CodeBox does this for you automatically:
async with boxlite.CodeBox() as cb:
    output = await cb.run("print(2 + 2)")
    # output is "4\n" (run() returns stdout as a string)

# ...but you could do the same thing with SimpleBox:
async with boxlite.SimpleBox(image="python:slim") as box:
    result = await box.exec("python", "-c", "print(2 + 2)")
    output = result.stdout
    # output is "4\n" (exec() returns an ExecResult with .stdout, .stderr, .exit_code)
For BrowserBox and ComputerBox, technically yes but practically no. You could use SimpleBox with the same images (e.g. SimpleBox(image="mcr.microsoft.com/playwright:v1.58.0-jammy")), but you’d have to manually set up port forwarding, start the browser server, wait for it to be ready, and connect — all things that BrowserBox handles in a single connect() call. The specialized APIs (screenshot(), mouse_move(), playwright_endpoint(), etc.) save significant work.

Lifecycle

Every box follows the same lifecycle: create → use → stop. The simplest pattern uses async with, which handles startup and cleanup automatically:
async with boxlite.SimpleBox(image="python:slim") as box:
    await box.exec("echo", "hello")
    # Box is automatically stopped when the block exits

Manual lifecycle

When you need more control (long-lived boxes, cross-function usage), manage the lifecycle explicitly:
box = boxlite.SimpleBox(image="python:slim")
await box.start()

try:
    await box.exec("echo", "hello")
finally:
    await box.shutdown()

What happens at each stage

StageWhat happens
CreateBoxLite pulls the OCI image (cached after first use), allocates resources, and boots a microVM. This typically takes under a second for cached images.
UseRun commands with exec() (SimpleBox, CodeBox, InteractiveBox) or run() (CodeBox only). Use specialized methods for BrowserBox (connect()) and ComputerBox (screenshot(), mouse_move(), etc.). Files you create and packages you install persist within the box. Note: each CodeBox.run() call starts a new Python process, so Python variables and function definitions do not carry over between calls.
StopThe VM shuts down and all resources are released. With auto_remove=True (the default), the box is fully cleaned up.

Images

BoxLite uses standard OCI container images — the same images you use with Docker. For box types that accept a custom image (SimpleBox, CodeBox, InteractiveBox), specify it when creating the box:
# Docker Hub images — use any image you want
boxlite.SimpleBox(image="python:slim")
boxlite.SimpleBox(image="alpine:latest")
boxlite.SimpleBox(image="ubuntu:22.04")
boxlite.SimpleBox(image="node:20-slim")

# CodeBox defaults to python:slim — no image needed
boxlite.CodeBox()
BrowserBox and ComputerBox use fixed images (Playwright and webtop respectively) that can’t be changed, because their convenience methods depend on specific software being pre-installed. The image is pulled on first use and cached locally. Subsequent starts reuse the cached image.
Use slim or minimal images (python:slim, alpine:latest) for faster boot times. Full images like ubuntu:latest work but are larger and slower to pull.

Resource configuration

Every box accepts CPU and memory parameters. The example below uses SimpleBox/CodeBox/InteractiveBox parameter names:
boxlite.SimpleBox(
    image="python:slim",
    cpus=2,             # CPU cores (default: 1)
    memory_mib=4096,    # Memory in MiB (default: 2048)
    disk_size_gb=10,    # Persistent disk in GB (default: ephemeral)
)
ParameterDefaultRangeNotes
cpus11 to host CPU countMore CPUs help with parallel workloads
memory_mib2048128–65536Increase for data-heavy workloads or large packages
disk_size_gbNone (ephemeral)Any positive integerSet only if you need data to survive a restart
BrowserBox and ComputerBox use slightly different parameter names: cpu (singular) and memory (instead of memory_mib). See their SDK reference pages for details.
For most use cases, the defaults work well. Increase memory when running data-heavy code or installing large packages, and add CPUs for compute-intensive workloads.

Security model

Each box is a real microVM — not a container. BoxLite provides multiple layers of isolation:
  • Hardware virtualization — KVM on Linux, Hypervisor.framework on macOS. Each box has its own kernel.
  • Jailer — restricts the VM process with seccomp filters and cgroups.
  • Network isolation — boxes get their own network namespace by default.
You can tune security with SecurityOptions presets:
from boxlite.boxlite import SecurityOptions

async with boxlite.SimpleBox(
    image="python:slim",
    security=SecurityOptions.maximum(),  # Maximum isolation
) as box:
    await box.exec("echo", "locked down")
SecurityOptions is currently imported from boxlite.boxlite, not from the top-level boxlite module. Available presets: development(), standard(), maximum().
For full details on the security architecture, see Security.

What’s next?