# 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](/docs/speed)). A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while the code waits and $0.08 with both CPUs busy ([pricing](/docs/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: ```ts import { 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); ``` ```python from withruntime import Sandbox code = "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: `timedOut` is `true` and 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](/docs/sandbox-environment#who-you-are)). ## 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. ```ts check import { 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); ``` ```python check from withruntime import Sandbox with 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](https://docs.astral.sh/uv/guides/install-python/)). This needs the internet on: ```bash no-run uv python install 3.13 uv run --python 3.13 job.py uv run --with rich job.py ``` ## Keep 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](/docs/javascript#code-interpreter)): ```ts check import { 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"]); // 6 ``` ```python check from withruntime import Sandbox with 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"]) # 6 ``` Python 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](/docs/images) 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. ```ts check import { 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); ``` ```python check from withruntime import Runtime runtime = 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](/docs/security)). For the whole threat list, see [running untrusted LLM code](/use-cases/run-untrusted-llm-code). ## Related - [Build a code interpreter for a chatbot](/use-cases/code-interpreter-for-chatbots) - [A data analysis agent](/use-cases/data-analysis-agent) - [Grade student code](/use-cases/grade-student-code) - [Turn off a sandbox's internet](/how-to/turn-off-sandbox-internet) - [Run R code in a sandbox](/languages/r) New accounts get 50 free sandbox hours, no card: ```bash no-run npx 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.