Runtime

nsjail vs Firecracker: process jail or microVM for untrusted code?

nsjail confines a process on the host kernel with namespaces, limits and seccomp; Firecracker gives code its own kernel in a KVM microVM.

Runtime puts each sandbox in its own Firecracker microVM, and runs the host side for you. A new sandbox ran its first Python command 351 ms after the create request at the median, over the public internet, on 24 September 2026 (speed), with network, CPU, memory and cost enforced on the host.

How nsjail works

nsjail is a "Linux process isolation tool using namespaces, resource limits, and seccomp-bpf syscall filters," published in Google's GitHub organization under Apache 2.0. Its README ends: "This is not an official Google product."

It builds a jail out of kernel features:

  • Namespaces: UTS, mount, PID, IPC, network, user, cgroup and time.
  • Filesystem: chroot() or pivot_root(), read-only bind mounts, a custom /proc and tmpfs.
  • Limits: wall time (600 seconds by default), CPU time, address space, open files and process count, plus cgroup v1 or v2 limits on memory, PIDs and CPU.
  • System calls: seccomp-bpf policies written in Kafel, such as ALLOW { read, write, exit, exit_group } DEFAULT KILL.
  • Network: an empty network namespace, a cloned MACVLAN interface, or userland networking through pasta.

It runs a program once, re-runs it in a loop for fuzzing, or listens on a TCP port and jails a fresh process per connection. Its README names CTF challenge hosting, fuzzing and desktop application sandboxing as uses. A typical run:

Terminal./nsjail -Mo --chroot / --user 99999 --group 99999 \  --time_limit 10 --rlimit_as 512 -- /usr/bin/python3 answer.py

Everything inside the jail still runs on the host's kernel. The namespaces change what the process can see; the seccomp policy narrows which system calls it can make; the calls it is allowed are handled by the same kernel as every other process on the machine.

How Firecracker works

Firecracker is a virtual machine monitor that asks the host's KVM for a virtual machine and boots a guest Linux kernel in it. The code's system calls go to that guest kernel. What reaches the host is the monitor's small device model, "only 5 emulated devices" in the project's count, and its own filtered system calls.

Firecracker also uses the same tools as nsjail, one layer out. Its jailer "sets up system resources that require elevated permissions (e.g., cgroup, chroot), drops privileges, and then exec()s into the Firecracker binary." The monitor then runs with per-thread seccomp filters. The design document assumes "all vCPU threads are considered to be running malicious code as soon as they have been started."

Side by side

nsjail Firecracker
What it is A process jail A virtual machine monitor for microVMs
Kernel the code uses The host's Its own guest kernel
Boundary Namespaces, rlimits, cgroups, seccomp-bpf Hardware virtualization, then a jailer and seccomp on the VMM
Host surface Every system call the policy allows 5 emulated devices, in the project's count, and the VMM's calls
Root inside Root in a user namespace, on the host kernel Root in its own machine
Input A host directory or / as the root filesystem A kernel image and a root filesystem image
Network Empty namespace, MACVLAN or pasta virtio-net; filtering is left to the host
Needs User namespaces enabled, or root KVM on the host
Start time No figure published Up to 125 ms to guest init, in its specification
Memory overhead No figure published Up to 5 MiB for the VMM threads, in its specification
Language, licence C++, Apache 2.0 Rust, Apache 2.0

Is nsjail enough for LLM-generated code?

It narrows the attack surface a great deal, and a tight seccomp policy can make a kernel bug hard to reach. The limit is the one every process jail shares: the code runs on the host kernel, so one exploitable bug in a system call the policy allows reaches the whole machine. General code needs many system calls, so a policy loose enough for Python, pip and a compiler leaves much of the kernel reachable.

A microVM moves the boundary. The code can have root and every system call, because they are answered by a kernel that belongs to a disposable machine. Reaching the host means breaking that kernel and then the monitor.

Firecracker does not do everything for you either. Its design document says it "does not perform any network traffic filtering" and that guest traffic "should be filtered at the host-level." A production service still needs that layer, plus leases, quotas and cleanup.

When each fits

  • Pick nsjail for known programs on your own Linux hosts: judge systems for a fixed compiler and test set, CTF services, fuzzing targets, and sandboxing desktop applications. It needs no KVM and starts a process, not a machine.
  • Pick Firecracker for code you cannot predict: model output, agents with a shell, user-submitted projects that install their own packages, or anything that wants root.
  • Use both as Firecracker itself does: jail the monitor process, and put the untrusted code inside the VM.

What Runtime runs for you

Runtime operates the host side: a Firecracker microVM per sandbox on dedicated servers, and the network filtering Firecracker leaves to the host. Every outbound connection goes through a proxy on the host, private and internal addresses are refused, and root in the sandbox cannot change the rules (security).

TypeScriptimport { Sandbox } from "withruntime";const code = "import os; print(os.getuid(), os.uname().release)"; // what the model wroteawait using sbx = await Sandbox.create({  network: { internet: false },  timeoutSeconds: 120,  onLeaseEnd: "stop",});await sbx.files.write("/workspace/answer.py", code);const run = await sbx.exec(["sudo", "python3", "answer.py"], { timeoutMs: 10_000 });console.log(run.exitCode, run.timedOut, run.stdout); // root, in its own kernel
Pythonfrom withruntime import Sandboxcode = "import os; print(os.getuid(), os.uname().release)"  # what the model wrotewith Sandbox.create(    network={"internet": False},    timeout_seconds=120,    on_lease_end="stop",) as sbx:    sbx.files.write("/workspace/answer.py", code)    run = sbx.exec(["sudo", "python3", "answer.py"], timeout_ms=10_000)    print(run.exit_code, run.timed_out, run.stdout)  # root, in its own kernel

The command timeout and the network switch do the work of nsjail's --time_limit and empty network namespace. The network rules and the lease are enforced on the host, outside the guest.

New accounts get 50 free sandbox hours, no card:

Terminalnpx withruntime sandbox run --trial -- sudo id

More: run untrusted LLM code, grade student code, what is egress control?, Firecracker vs gVisor.

Sources

Checked 25 September 2026.

Facts on this page were checked on 25 September 2026.