Runtime

How to run smolagents code in a secure sandbox

Wrap Runtime's four sandbox functions with smolagents' @tool, and every command the agent runs executes in a disposable microVM.

On Runtime the sandbox is a whole Linux machine, not a Python snippet runner. smolagents' remote executors send the agent's Python snippets away and return their output. A Runtime sandbox is a Firecracker microVM with its own kernel, Ubuntu 24.04, Python 3.12, pip and sudo, so the agent can install packages, run test suites and keep files between steps. The median sandbox answered its first Python command 351 ms after the create request on 24 September 2026, and a 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while the model is thinking. smolagents 1.26.0 was the current PyPI release on 25 September 2026.

Where smolagents runs code today

A CodeAgent writes Python and hands it to an executor. The executor is chosen with executor_type, and smolagents 1.26.0 accepts five values:

executor_type Where the snippet runs What you set up
local (default) Your own process, in smolagents' AST-walking LocalPythonExecutor Nothing; imports need an allow-list
docker A Docker container on your machine Docker installed, smolagents[docker]
e2b An E2B sandbox An E2B account, smolagents[e2b]
modal A Modal sandbox A Modal account, smolagents[modal]
blaxel A Blaxel sandbox A Blaxel account, smolagents[blaxel]
Runtime tools A Runtime microVM, through tool calls withruntime; one browser approval to start

The smolagents guide is direct about the default: "no local python sandbox can ever be completely secure", and "the only way to run LLM-generated code with truly robust security isolation is to use remote execution options". It also notes that the remote executors do not support managed agents (multi-agent setups) yet.

Give a ToolCallingAgent the sandbox

sandbox_tools(sbx) returns runtime_exec, runtime_read_file, runtime_write_file and runtime_list_files as typed functions whose docstrings carry an Args: section. That is exactly what smolagents' tool decorator needs to build a tool's name, inputs and output type, so wrapping them is one line.

Terminalpip install smolagents withruntime
Pythonfrom smolagents import ToolCallingAgent, toolfrom withruntime import Sandboxfrom withruntime.tools import sandbox_toolsdef solve(model, task: str) -> str:    with Sandbox.create(        network={"internet": True, "allow": ["pypi.org", "*.pythonhosted.org"]},        timeout_seconds=900,        on_lease_end="stop",    ) as sbx:        tools = [tool(f) for f in sandbox_tools(sbx)]        agent = ToolCallingAgent(tools=tools, model=model, max_steps=15)        return str(agent.run(task))

model is any smolagents model, such as InferenceClientModel() or OpenAIModel(...). The sandbox exists for the length of the with block, so a package installed in step two is still there in step nine, and it stops when the block ends, error or not.

Prefer ToolCallingAgent here. A CodeAgent accepts the same tools and can call them from its code, but the snippets it writes still run in whichever executor executor_type names, which is your own process by default.

Check the tools before paying for a model

Each wrapped tool is callable on its own, so a smoke test needs no model:

Pythonfrom smolagents import toolfrom withruntime import Sandboxfrom withruntime.tools import sandbox_toolswith Sandbox.create() as sbx:    run, read, write, ls = (tool(f) for f in sandbox_tools(sbx))    print(run.inputs)  # command, cwd, timeout_seconds    print(write(path="fib.py", content="a, b = 0, 1\nfor _ in range(90): a, b = b, a + b\nprint(a)\n"))    print(run(command="python3 fib.py"))  # {'exit_code': 0, 'stdout': '...', ...}

runtime_exec returns the exit code, stdout, stderr and a timed_out flag, so the model sees a failure as a failure. Relative paths land in /workspace.

Run the whole agent in the sandbox

smolagents' guide describes a second pattern for multi-agent systems: put the agent, its model calls and its tools all inside the sandbox. Its drawback, in the guide's words, is that it "may require transferring sensitive API keys to the sandbox environment". Runtime removes that step. Store the model key once as a secret bound to the provider's host:

Terminalprintf %s "$OPENAI_API_KEY" | npx withruntime secrets set OPENAI_API_KEY --host api.openai.com

Every sandbox of the account then has OPENAI_API_KEY set to a placeholder. The host's proxy swaps in the real key only on HTTPS requests to api.openai.com, so managed agents inside the sandbox can call the model while nothing in the machine can read the key (secrets sandboxes never see).

Pythonimport sysfrom withruntime import Sandboxtask = "What's the 20th Fibonacci number?"with Sandbox.create(    network={"internet": True, "allow": ["api.openai.com", "pypi.org", "*.pythonhosted.org"]},    timeout_seconds=1800,    on_lease_end="stop",) as sbx:    sbx.files.upload("./my_agents", "/workspace/my_agents")    sbx.exec("pip install 'smolagents[openai]'", check=True, timeout_ms=300_000)    run = sbx.exec(        ["python3", "manager.py", task],        cwd="/workspace/my_agents",        timeout_ms=1_200_000,        on_stdout=sys.stdout.write,    )    print(run.exit_code)

manager.py is your own smolagents program; it uses OpenAIModel as it would on your laptop. The allow-list keeps the sandbox to the model's API and the package index.

Keep keys and spending in check

  • The model never holds your Runtime key. Your code creates the sandbox and binds the tools; the model only chooses commands and paths.
  • Cap each agent run. timeout_seconds with on_lease_end="stop" ends a forgotten sandbox. max_cost_micros refuses a create whose first lease would cost more than you set.
  • Cap the key. Give the application a key with a daily spending limit; past it, creates fail with spending_limit_reached and nothing is charged (read-only keys and daily limits).

Which executor fits

Choose local only for trusted demos. Choose Docker when everything stays on one machine you control. Choose Runtime when the agent needs a real shell, many agents run at once, or managed agents need a model key without holding it. A paid account runs 100 sandboxes at once to start, billed on the CPU they use: $0.08 an hour for a 2 vCPU, 4 GiB sandbox with both CPUs busy (pricing).

For the other Python frameworks see any other framework; for the risks being contained, see run untrusted LLM code; for container isolation compared with a microVM, see microVM vs container. New accounts get 50 free sandbox hours, no card:

Terminalnpx withruntime sandbox run --trial -- python3 -c 'print(6 * 7)'

Sources

Facts on this page were checked on 25 September 2026.