Which should you use, WebAssembly or a microVM?
It depends on who writes the code and what it expects. For code an agent or model wrote, which expects a shell, packages, subprocesses and the network, Runtime gives it a Firecracker microVM. Pick WebAssembly for plugins, user-defined functions and edge handlers you compile yourself, with a narrow interface where the host grants every capability.
How WebAssembly isolates code
WebAssembly is a bytecode format run by a runtime such as Wasmtime. The specification's security page says "each WebAssembly module executes within a sandboxed environment separated from the host runtime using fault isolation techniques." A module has its own linear memory, and accesses to it are bounds-checked. Calls go only to declared functions, and indirect calls are type-checked. A module can reach the outside world only through the functions its host chose to import.
WASI extends that to system resources. In its words, a module "starts with no ambient authority and can only do what the host explicitly grants." A host that grants no directory gives the module no files. One that grants no sockets gives it no network.
Wasmtime adds defence in depth on top: a 2 GB guard region before each linear memory, memory zeroed after an instance ends where it can be, Spectre mitigations on bounds checks, and continuous fuzzing through Google's OSS-Fuzz.
How a microVM isolates code
A microVM runs a guest Linux kernel under a hypervisor. Firecracker, which Runtime uses, asks the host's KVM for a virtual machine. It gives the guest what its site counts as "only 5 emulated devices". The code inside can do anything Linux allows, root included, because the boundary is the virtual machine itself. Firecracker's design document starts from the worst case: "all vCPU threads are considered to be running malicious code as soon as they have been started."
Side by side
| WebAssembly (Wasmtime with WASI) | MicroVM (Firecracker) | |
|---|---|---|
| Unit of isolation | One module in a runtime, often inside your own process | A virtual machine with its own Linux kernel |
| Boundary | Software fault isolation, validated by the runtime | Hardware virtualization through KVM |
| What the code can call | Only functions the host imports; WASI grants per capability | Any Linux system call, answered by the guest kernel |
| What reaches the host | The runtime's code and every host function you expose | 5 emulated devices, in the project's count |
| Languages | Anything compiled to Wasm; Python needs a Wasm build | Any Linux program, unchanged |
| Packages | Must be built for Wasm; native extensions need cross-compile | apt, pip, npm as on any Linux machine |
| Processes and threads | Pyodide, CPython's Wasm port, cannot start either | Normal Linux processes and threads |
| Start time | No single figure published by Wasmtime | Up to 125 ms to guest init, in Firecracker's specification |
| Gaps the spec names | Code reuse, TOCTOU races, timing side channels | The guest kernel is the code's own; the host's is not shared |
Is WebAssembly safe enough for LLM code?
For a small, pure function compiled to Wasm and given no imports, the sandbox is strong and cheap. The question is what the code needs, because each import is a hole you cut on purpose.
Model-written code rarely stays pure. It opens files, installs packages, calls APIs and starts processes. Each of those becomes a host function that your Wasm host implements and has to get exactly right. The specification says "other classes of bugs are not obviated by the semantics of WebAssembly": code reuse attacks against indirect calls, time-of-check to time-of-use races, and side channels such as timing attacks.
The host layer is where real bugs land. On 20 August 2026 Wasmtime published a High-severity advisory, GHSA-vqjp-4c8c-hfgg, "Filesystem sandbox escape when paths or symlinks contain trailing slashes", in its WASI implementation. That was a flaw in the capability layer, not in Wasm itself. That is the point: the more of the operating system you expose, the more of an operating system you are writing.
A microVM moves that work into a real Linux kernel that the code owns. If the code breaks the guest kernel, it has broken only its own disposable machine.
Can WebAssembly run Python or pip install?
Only in a Wasm build. Pyodide ports CPython to WebAssembly. Its package
installer takes pure-Python wheels and packages cross-compiled for Pyodide,
and threading, multiprocessing and subprocess raise RuntimeError.
Pyodide vs a server sandbox covers the
limits in full. A microVM runs the Python on the machine, so any wheel on PyPI
installs.
When each fits
- Pick WebAssembly for plugins and user-defined functions inside your own service, edge handlers, and code you compile yourself with a narrow, known interface, where the host decides every capability.
- Pick a microVM for code an agent or model wrote in an ordinary language, which expects a shell, a package manager, a filesystem, subprocesses and the network, and which you cannot review before it runs.
Runtime Cloud
Runtime gives model-written code a whole Firecracker microVM, so it runs as
written: Python, Node.js, shell, pip install, subprocesses.
Runtime gives each sandbox its own Firecracker microVM with Ubuntu 24.04, Python 3.12, Node.js 24, Bun, git and gcc. The controls a Wasm host would implement live on the Runtime host, outside the guest, so root inside cannot change them (security):
- Network: off, or narrowed to named hosts. Private addresses are always refused (turn off sandbox internet).
- Time: commands time out, and the sandbox stops at the time limit you set.
- Money:
maxTotalCostMicrosstops a sandbox once it has cost that much in all.
TypeScriptimport { Sandbox } from "withruntime";const code = "import subprocess; print(subprocess.run(['uname', '-r'], capture_output=True, text=True).stdout)";await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 300, onLeaseEnd: "stop",});await sbx.files.write("/workspace/answer.py", code);const run = await sbx.exec(["python3", "answer.py"], { timeoutMs: 30_000 });console.log(run.exitCode, run.stdout);Pythonfrom withruntime import Sandboxcode = "import subprocess; print(subprocess.run(['uname', '-r'], capture_output=True, text=True).stdout)"with Sandbox.create( network={"internet": False}, timeout_seconds=300, on_lease_end="stop",) as sbx: sbx.files.write("/workspace/answer.py", code) run = sbx.exec(["python3", "answer.py"], timeout_ms=30_000) print(run.exit_code, run.stdout)The model's subprocess call works, because the sandbox is a real Linux
machine. The network stays off whatever the code tries.
More: microVM vs container, run untrusted LLM code, what is a microVM?, unikernel vs microVM.
Try it free: 100 free hours of a 1 GB machine in the first 30 days, no card:
Terminalnpx withruntime sandboxes run -- python3 -c 'import subprocess; print(subprocess.run(["id"]))'Sign in with npx withruntime login and try it on the free usage. Code on
another sandbox today moves with the migration guide.
Sources
Checked 26 October 2026.
- WebAssembly security, WASI
- Wasmtime, Wasmtime security, Wasmtime fuzzing, Wasmtime security advisories
- Pyodide WebAssembly constraints, Pyodide FAQ
- Firecracker, specification, design
Facts on this page were checked on 26 October 2026.