How to run untrusted Python code safely
Run it in a throwaway Linux microVM with its own kernel, the internet off, a command timeout and a lease that ends on its own.
On Runtime, Python is already there and the first result comes back in about a third of a second. Every sandbox is a Firecracker microVM with Python 3.12, pip, uv, NumPy, pandas and matplotlib in the image. A new sandbox ran its first Python command 351 ms after the create request at the median (p95 815 ms, 20 runs, 24 September 2026, speed). A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while the code waits and $0.08 with both CPUs busy (pricing).
Run a Python file from your program
Write the code into the sandbox, then run it with an array, so no shell ever parses it:
TypeScriptimport { Sandbox } from "withruntime";const code = "import sys\nprint(sys.version)\nprint(sum(range(100)))";await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 300, onLeaseEnd: "stop",});await sbx.files.write("/workspace/job.py", code);const run = await sbx.exec(["python3", "job.py"], { timeoutMs: 30_000 });console.log(run.exitCode, run.timedOut, run.stdout, run.stderr);Pythonfrom withruntime import Sandboxcode = "import sys\nprint(sys.version)\nprint(sum(range(100)))"with Sandbox.create( network={"internet": False}, timeout_seconds=300, on_lease_end="stop",) as sbx: sbx.files.write("/workspace/job.py", code) run = sbx.exec(["python3", "job.py"], timeout_ms=30_000) print(run.exit_code, run.timed_out, run.stdout, run.stderr)- A timeout is a result, not an exception:
timedOutistrueand you get the output so far. The default is 60 seconds, the maximum 24 hours. - A result holds at most 64 KiB of stdout and 64 KiB of stderr. Longer output
streams through
onStdout(on_stdout) or goes to a file you read back. - The sandbox stops when the block ends, even after an error.
What Python has in the image
| What | Version or detail |
|---|---|
| Operating system | Ubuntu 24.04.5 LTS (noble), amd64, 6.1 kernel |
| Python | 3.12, as python3 and python, with pip, pip3, venv |
| uv | 0.12.17 (uv, uvx) |
| Data tools | NumPy 1.26.4, pandas 2.1.4, matplotlib 3.6.3 |
| C toolchain | gcc, g++, make, for packages that build from source |
| User | runtime (uid 1000), home /workspace, sudo works |
pip install works without a virtual environment. As the sandbox user it
installs to /workspace/.local, whose bin is first on PATH, and the
image's pip settings let it past the Ubuntu rule against installing into the
system Python (PEP 668). Use python3 -m venv or uv when you want
isolation (the sandbox environment).
Install packages, then cut the network
Open the network only to the package index, install, and switch it off before the untrusted code runs. A rule applies at once, to open connections too.
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ network: { internet: true, allow: ["pypi.org", "*.pythonhosted.org"] }, timeoutSeconds: 600, onLeaseEnd: "stop",});await sbx.exec("pip install scikit-learn", { check: true, timeoutMs: 300_000 });await sbx.network.set({ internet: false });await sbx.files.write("/workspace/model.py", "import sklearn\nprint(sklearn.__version__)");const run = await sbx.exec(["python3", "model.py"], { timeoutMs: 60_000 });console.log(run.stdout);Pythonfrom withruntime import Sandboxwith Sandbox.create( network={"internet": True, "allow": ["pypi.org", "*.pythonhosted.org"]}, timeout_seconds=600, on_lease_end="stop",) as sbx: sbx.exec("pip install scikit-learn", check=True, timeout_ms=300_000) sbx.network.set(internet=False) sbx.files.write("/workspace/model.py", "import sklearn\nprint(sklearn.__version__)") print(sbx.exec(["python3", "model.py"], timeout_ms=60_000).stdout)Give installs a timeoutMs above the 60-second default.
Another Python version
The image ships 3.12. uv installs other versions from the python-build-standalone project and runs a script on one (uv). This needs the internet on:
Terminaluv python install 3.13uv run --python 3.13 job.pyuv run --with rich job.pyKeep variables between runs
A chat assistant or a data agent usually wants a notebook, not a fresh process per answer. The code interpreter keeps Python state between cells and returns matplotlib charts as PNG and pandas data frames as tables (code interpreter):
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ network: { internet: false } });await sbx.interpreter.run("import pandas as pd\ndf = pd.DataFrame({'a': [1, 2, 3]})");const cell = await sbx.interpreter.run("df['a'].sum()");console.log(cell.results[0]?.data["text/plain"]); // 6Pythonfrom withruntime import Sandboxwith Sandbox.create(network={"internet": False}) as sbx: sbx.interpreter.run("import pandas as pd\ndf = pd.DataFrame({'a': [1, 2, 3]})") cell = sbx.interpreter.run("df['a'].sum()") print(cell["results"][0]["data"]["text/plain"]) # 6Python is the interpreter's default language and is in the image, so a cell needs no install and no network.
Start every sandbox with your packages
Build a custom image once and every sandbox made from it starts with the packages installed. Building is free; a stored image is charged on its size, and the free trial stores its first three free.
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "py-ml", recipe: { pip: ["scikit-learn", "polars"] },});await using sbx = await runtime.sandboxes.create({ image: "py-ml", network: { internet: false } });console.log((await sbx.exec(["python3", "-c", "import polars; print(polars.__version__)"])).stdout);Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build(name="py-ml", recipe={"pip": ["scikit-learn", "polars"]})with runtime.sandboxes.create(image="py-ml", network={"internet": False}) as sbx: print(sbx.exec(["python3", "-c", "import polars; print(polars.__version__)"]).stdout)Why not exec() or a subprocess on your server?
Code run with exec() or a subprocess runs as your server, with its files,
memory, network and credentials. A container shares your host's
kernel. A microVM gives the code its own kernel behind hardware
virtualization, and on Runtime root inside it still cannot change the network
rules, CPU, memory or cost, which the host enforces
(security). For the whole threat list, see
running untrusted LLM code.
Related
- Build a code interpreter for a chatbot
- A data analysis agent
- Grade student code
- Turn off a sandbox's internet
- Run R code in a sandbox
New accounts get 50 free sandbox hours, no card:
Terminalnpx withruntime sandbox run --trial -- python3 -c 'import pandas; print(pandas.__version__)'Sources
- uv, installing Python and running scripts: https://docs.astral.sh/uv/guides/install-python/ and https://docs.astral.sh/uv/guides/scripts/ (read 25 September 2026)
Facts on this page were checked on 25 September 2026.