Skip to main content
Production means four things at once: every agent session gets its own disposable microVM, tool calls stay sandboxed, resources are bounded, and failures are catchable rather than fatal.

Intent

Once you have run a demo in which an agent calls a single tool inside BoxLite, the next and harder problem is this: how do you turn that demo into a production agent that can carry real traffic? In a production setup, each agent session gets its own fast-starting, disposable microVM; tool calls are sandboxed; resources are bounded by quotas; secrets never enter the prompt; and failures can be caught and retried. This page is a navigation page. It does not document individual APIs (those live on the feature pages). Instead it lays out a route from scattered capabilities to a production-grade agent, and points to the docs-v2 page for each step. After reading this page you should be able to answer:
  1. Why give each agent session its own sandbox? (isolation, quotas, rollback)
  2. What does a production agent consist of? (sandbox + tool layer + secrets + lifecycle governance + observability)
  3. In what order do you fill in those pieces, from demo to production?
  • You have read Quickstart (Python) and
  • You understand the box lifecycle mental model (see Manage Sandbox): the Boxlite runtime is a synchronous context manager, while SimpleBox / CodeBox and the other box types are asynchronous context managers.

Mental model: one agent session = one sandbox

An LLM agent is a loop: the model emits a tool call -> it is executed somewhere -> the result is returned to the model -> repeat. Replacing “somewhere” with a BoxLite box gives you the capabilities a production agent needs: The core principle: one agent session maps to one box; when the session ends, the box is discarded. Under high concurrency, give each user/session its own named box and let the runtime govern them.

Quick Example (minimal happy path)

Below is a minimal, runnable tool-execution loop for an agent: it runs the command “the model wants to execute” inside a sandbox, checks the exit code, and formats the result into a string you can feed back to the model. A hardcoded tool call stands in for real LLM output so you can copy and run it directly; when you wire in a real model, replace tool_call with the model’s returned tool call.
Node differs in three places: the package name is @boxlite-ai/boxlite, SimpleBox cleans up via await using, and the exec timeout option is timeoutSecs.
For a complete tool loop where a real LLM drives the sandbox, see Drive a sandbox from your agent loop.

From demo to production: a step-by-step route

You can promote a demo to a production agent in the following five steps. Each step states the problem it solves and the feature page it maps to.

Step 1: Choose the right Box type

Different agent tools map to different box types; you do not have to assemble everything from a bare SimpleBox. For an overview and selection guidance per type, see Box types.

Step 2: Set resource quotas and isolation for tool calls

A production agent must assume the model will generate runaway code, and should use quotas and security options to bound its impact.
  • Configure cpus / memory_mib / disk_size_gb (if not passed explicitly, the underlying engine allocates a default; setting them explicitly is recommended in production): see Compute Resources.
  • Tighten networking (all outbound is allowed by default; you can switch to an allowlist or disable it entirely): see Network Access.
  • Enable the strongest isolation: BoxOptions(advanced=AdvancedBoxOptions(security=SecurityOptions.maximum())): see Secrets and Security.

Step 3: Provide secrets to tools without putting them in the prompt

An agent’s tools often need API keys (search, databases, third-party services). Do not splice secrets into the model’s prompt or into bare environment variables. Use Secret, which scopes usage to a target host and injects the value via a placeholder.
For details, see Secrets and Security.

Step 4: Govern the session lifecycle

Under high concurrency you have one box per session, and the runtime (Boxlite / JsBoxlite) is responsible for its creation, reuse, and reclaim.
  • One-shot sessions: use the wrapper’s async with + auto_remove=True (default); discard on exit.
  • Long-lived / cross-process reuse: use named boxes with runtime.get_or_create(...), and explicitly call runtime.remove(id_or_name, force=) to reclaim.
  • Key constraint: removal and listing are the runtime’s responsibility --- runtime.remove(...) / runtime.list_info(), not box.remove() / runtime.list().
For the complete lifecycle model and code, see Manage Sandbox - Lifecycle.

Step 5: Add observability and state rollback

A production agent should support monitoring and recover from bad states.
  • Metrics: runtime.metrics() exposes RuntimeMetrics.num_running_boxes / boxes_created_total / boxes_failed_total; box.metrics() exposes per-session BoxMetrics.cpu_percent / memory_bytes.
  • Checkpoints and retries: call box.snapshot.create(...) on a half-executed box, and after an error use restore(...) to return to the checkpoint instead of re-running from scratch. See Snapshots.
When you are ready to ship the service, see Deploy in Docker or Kubernetes for running it in Docker or Kubernetes.

Parameters and Returns (key entry points per step)

The table below summarizes the core API entry points used in this route, so you can jump to the corresponding feature page for parameter details.
The exec timeout parameter differs in name/type across surfaces: the wrapper SimpleBox.exec(timeout=...) takes a float (seconds); Node uses { timeoutSecs }; the native Box.exec uses timeout_secs. CodeBox.run’s timeout is an int.

ExecResult (return value of each tool call)


Troubleshooting

An agent tool appears not to error, but has actually failed

When an exec command exits with a non-zero code, it does not raise; instead it returns ExecResult(exit_code != 0). If your tool-execution function does not check the exit code, it will report a failure to the model as a success, and the agent’s output will drift step by step. Check result.exit_code (Node: result.exitCode) after every tool call, and feed the failure back as an observation so the model can self-correct.

A missing command / image pull failure does not raise BoxliteError

A missing command or an image pull failure raises a standard RuntimeError (Python) / bare Error (Node), not BoxliteError. See Error Handling.

A Secret value was spliced directly into the prompt

The point of Secret is to keep the secret out of the prompt and out of logs. Do not write the cleartext into the system prompt so the model “knows” a key exists. Put the secret in BoxOptions(secrets=[Secret(...)]), and let the tool use it inside the sandbox via environment variable/placeholder. See Secrets and Security.

Calling the nonexistent box.remove() / runtime.list()

This is easy to hit during high-concurrency governance: removal and listing are the runtime’s responsibility.
  • Use runtime.remove(id_or_name, force=False), not box.remove().
  • Use runtime.list_info(), not runtime.list().

Confusing the synchronous and asynchronous context managers

  • The Boxlite runtime’s context-manager protocol is synchronous: use with Boxlite(...) as runtime:, not async with (it has no __aenter__/__aexit__). This constraint applies only to entering/leaving the context; the runtime’s data methods (create / get_or_create / list_info / remove / metrics / shutdown) still require await under the async API.
  • SimpleBox / CodeBox and the other boxes are async async with, and their methods require await (with the exception of Box.info(), which is always synchronous --- do not await it).
  • Using async with on Boxlite, or a plain with on SimpleBox, raises a context-manager protocol error.

No resource quotas set, so one runaway session drags down the host

When cpus / memory_mib are not passed, SimpleBox forwards the fields to the underlying engine, which allocates a default. A production agent should set cpus / memory_mib quotas explicitly per session for predictability and cost control, and estimate total resource use against expected concurrency. See Compute Resources.

The third volume element was a string instead of a bool

If a tool needs to mount a host directory (such as a code repository), the third element of a volume is the bool read_only (True = read-only / False = read-write), not a string. Passing "ro" / "rw" raises:
Correct form: volumes=[("/host/repo", "/work", True)] (read-only) or a 2-tuple (default read-write). This differs from the CLI’s -v host:box:ro string syntax --- that belongs to the CLI parsing layer, while the SDK API uses a bool. See Volumes.

Start fails: no hardware virtualization

BoxLite needs hardware virtualization to bring up the per-session microVM:
  • Linux: needs KVM (/dev/kvm accessible; on WSL2, KVM must be enabled and the user must be in the kvm group).
  • macOS: uses Apple Hypervisor.framework, no /dev/kvm required. macOS Intel is not supported.
  • No-virtualization environments (some containers / CI): start() fails and raises, but the process stays alive --- catch it with try/except to degrade gracefully on CI without virtualization.
Platform support: macOS ARM64 (yes) · Linux x86_64 (yes) · Linux ARM64 (yes) · Windows WSL2 (yes) · macOS Intel (not supported).

Next steps