Skip to content
Runtime

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).

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:

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. 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).

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.

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 with 3.12. uv installs other versions from the python-build-standalone project, and it runs a script on one of them (uv). This needs internet access:

Terminaluv python install 3.13uv run --python 3.13 job.pyuv 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):

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"]); // 6
Pythonfrom 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"])  # 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 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.

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. 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). For the full list of threats, see running untrusted LLM code.

A new account gets 100 free hours of a 1 GB machine in its first 30 days. No payment card is needed:

Terminalnpx withruntime sandboxes run -- python3 -c 'import pandas; print(pandas.__version__)'

Sources

Facts on this page were checked on 26 October 2026.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card