# 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 sandbox that pauses itself when idle. **On Runtime, Python is already there: the first command after the create runs it.** Every sandbox is a Firecracker microVM. The image includes Python 3.12, pip, uv, NumPy, pandas and matplotlib. 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 to the sandbox. Then run it with an array command, so no shell 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 is 24 hours. - A result holds at most 64 KiB of stdout and 64 KiB of stderr. Longer output streams through `onStdout` (`on_stdout`), or it goes to a file that you read back. - The sandbox is deleted when the block ends, even after an error. To stop it instead and keep its disk, pass `keep=True` to `create`. ## What Python has in the image | What | Version or detail | | ---------------- | --------------------------------------------------------- | | Operating system | Ubuntu 24.04.5 LTS (noble), amd64, 6.18 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` folder is first on `PATH`. The image's pip settings allow installs 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 turn the network off before the untrusted code runs. A rule applies immediately, including to open connections. ```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 with 3.12. uv installs other versions from the python-build-standalone project, and it runs a script on one of them ([uv](https://docs.astral.sh/uv/guides/install-python/)). This needs internet access: ```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 access. ## Start every sandbox with your packages Build a [custom image](/docs/images) once. Every sandbox made from it starts with the packages installed. Building is free. A stored image is billed by its size. An account without credit stores its first three images at no charge. ```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. It has your server's files, memory, network and credentials. A container shares your host's kernel. A microVM gives the code its own kernel, behind hardware virtualization. On Runtime, root inside the microVM still cannot change the network rules, CPU, memory or cost. The host enforces those ([security](/docs/security)). For the full list of threats, 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) A new account gets 100 free hours of a 1 GB machine in its first 30 days. No payment card is needed: ```bash no-run npx withruntime sandboxes run -- 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 26 October 2026) Facts on this page were checked on 26 October 2026.