Sandbox Untrusted LLM-Generated Code with gVisor and Firecracker A new tutorial by Ji-ho Choi demonstrates how to sandbox untrusted LLM-generated code using gVisor containers and Firecracker microVMs, providing a local execution environment for AI agents. The setup includes a shell wrapper that runs model-generated Python in a gVisor-isolated container with no network and resource caps, and a Firecracker path for hardware virtualization. Verified against gVisor release-20260727.0, Firecracker v1.16.1, Docker Engine 29.7, and python:3.14-alpine. Sandbox Untrusted LLM-Generated Code with gVisor and Firecracker Give your agent a safe place to run arbitrary code: gVisor containers for speed, Firecracker microVMs for hard isolation. Ji-ho Choi https://sourcefeed.dev/u/jiho choi What you'll build A local execution sandbox for LLM agents: a shell wrapper that runs model-generated Python inside a gVisor https://gvisor.dev/ -isolated container with no network and hard resource caps, plus a Firecracker https://firecracker-microvm.github.io/ microVM path for when you want a full hardware-virtualization boundary. Your agent pipes code in, gets stdout back, and the host never trusts a single syscall. Prerequisites - A Linux host, x86 64 or arm64. gVisor needs kernel 4.14.77+, so any current distro works; its default systrap platform doesn't need KVM. The Firecracker steps do need read/write access to /dev/kvm — bare metal or a cloud instance with nested virtualization. - Docker Engine and sudo access. Commands assume Ubuntu 24.04 LTS. curl , wget , and squashfs-tools sudo apt-get install -y squashfs-tools for the Firecracker rootfs step.- Verified August 2026 against gVisor release-20260727.0, Firecracker v1.16.1, Docker Engine 29.7, and the python:3.14-alpine image. 1. Install gVisor and register the runsc runtime gVisor is an application kernel: syscalls from your workload are intercepted and served by a userspace kernel written in Go, so untrusted code never talks to the host kernel directly. It ships as an OCI runtime called runsc that plugs straight into Docker https://docs.docker.com/engine/ . sudo apt-get update && \ sudo apt-get install -y apt-transport-https ca-certificates curl gnupg curl -fsSL https://gvisor.dev/archive.key | \ sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg echo "deb arch=$ dpkg --print-architecture signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg https://storage.googleapis.com/gvisor/releases release main" | \ sudo tee /etc/apt/sources.list.d/gvisor.list /dev/null sudo apt-get update && sudo apt-get install -y runsc Register it as a Docker runtime and restart the daemon — runsc install writes the runtime entry into /etc/docker/daemon.json for you: sudo runsc install sudo systemctl restart docker docker run --rm --runtime=runsc hello-world If hello-world prints its usual banner, the runtime is wired up. 2. Wrap runsc in a locked-down executor gVisor removes the shared-kernel risk; Docker flags remove everything else the code doesn't need. Save this as run-untrusted.sh : bash /usr/bin/env bash Reads Python source on stdin, executes it in a gVisor sandbox. set -euo pipefail exec docker run --rm -i \ --runtime=runsc \ --network=none \ --memory=256m --cpus=0.5 --pids-limit=64 \ --read-only --tmpfs /tmp:size=16m \ --cap-drop=ALL --security-opt=no-new-privileges \ python:3.14-alpine \ timeout 10 python3 - chmod +x run-untrusted.sh echo 'print sum range 100 ' | ./run-untrusted.sh Each flag closes a hole: --network=none gives the sandbox only a loopback interface, so exfiltration and reverse shells are dead on arrival; --pids-limit kills fork bombs; --read-only plus a small tmpfs means nothing persists between runs; --cap-drop=ALL strips capabilities even inside gVisor's kernel; timeout 10 bounds infinite loops the container exits non-zero and --rm cleans it up . Your agent calls this script per snippet — fresh container every time, nothing shared. 3. Fetch Firecracker, a kernel, and a rootfs gVisor's boundary is a hardened userspace kernel. If your threat model wants hardware virtualization — the same line AWS Lambda draws around customer code — use Firecracker, a KVM-based VMM that boots a minimal VM in ~125 ms. First confirm KVM access, then grab the latest release binary: lsmod | grep kvm -r /dev/kvm && -w /dev/kvm && echo "OK" || echo "FAIL" ARCH="$ uname -m " release url="https://github.com/firecracker-microvm/firecracker/releases" latest version=$ basename $ curl -fsSLI -o /dev/null -w %{url effective} ${release url}/latest curl -L ${release url}/download/${latest version}/firecracker-${latest version}-${ARCH}.tgz | tar -xz mv release-${latest version}-${ARCH}/firecracker-${latest version}-${ARCH} firecracker A microVM needs a guest kernel and a root filesystem. Firecracker's CI publishes both; these commands pick the newest matching your release line: CI VERSION=${latest version%. } latest kernel key=$ curl "http://spec.ccfc.min.s3.amazonaws.com/?prefix=firecracker-ci/$CI VERSION/$ARCH/vmlinux-&list-type=2" \ | grep -oP " ?<=