# 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. ```bash no-run pip install smolagents withruntime ``` ```python check from smolagents import ToolCallingAgent, tool from withruntime import Sandbox from withruntime.tools import sandbox_tools def 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: ```python check from smolagents import tool from withruntime import Sandbox from withruntime.tools import sandbox_tools with 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: ```bash no-run printf %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](/docs/security#secrets-sandboxes-never-see)). ```python check import sys from withruntime import Sandbox task = "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](/docs/security#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](/docs/pricing)). For the other Python frameworks see [any other framework](/docs/frameworks#any-other-framework); for the risks being contained, see [run untrusted LLM code](/use-cases/run-untrusted-llm-code); for container isolation compared with a microVM, see [microVM vs container](/compare/microvm-vs-container). New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- python3 -c 'print(6 * 7)' ``` ## Related - [LLM tool use in a sandbox](/use-cases/llm-tool-use-sandbox) - [What is a code interpreter?](/glossary/code-interpreter) ## Sources - [smolagents: Secure code execution](https://huggingface.co/docs/smolagents/tutorials/secure_code_execution), read 25 September 2026 - [smolagents on PyPI](https://pypi.org/project/smolagents/), version 1.26.0, read 25 September 2026; `CodeAgent`'s `executor_type` values and the `@tool` schema were read from that release's source Facts on this page were checked on 25 September 2026.