Originally published at kunalganglani.com — read it there for inline code, hero image, and live links.
AI agent sandbox linux vm setups are the fastest way to turn “LLM tool use” from a cool demo into something you can run without sweating every command. The moment your agent can execute a shell, install packages, or curl the internet, you’ve created a tiny production-incident generator. My stance is simple: one disposable Linux VM per agent run, with default-deny network egress, snapshot rollback, and auditable logs.
Key takeaways
- Treat an agent run like running untrusted code. Because functionally, that’s what it is.
- Default-deny outbound egress is the only policy that survives prompt injection and supply-chain surprises.
- Use a base image plus copy-on-write overlays so every run resets in seconds.
- Inject secrets as short-lived, least-privilege credentials. Don’t bake them into images. Don’t leave them on disk.
- Persist only what you can defend in a post-incident review: artifacts and audit bundles. Throw the rest away.
Give your agent tools only inside a disposable VM, and treat the VM like it’s already compromised.
Inline illustration suggestion: Diagram showing “Agent Orchestrator” launching a disposable VM, with arrows for allowlisted egress, artifact export, and log bundle export.
What is an AI agent sandbox?
An AI agent sandbox is an isolated execution environment that lets an agent use real tools (shell, Git, package managers, browsers) while limiting blast radius: file access, network access, and credentials. In practice, it’s a box you’re comfortable letting get owned.
When people say “sandbox,” they often mean “a Docker container with vibes.” That’s fine for toy demos. For serious tool use, I want something that actually holds up when the agent goes off-script.
At minimum, I’m looking for:
- A hard boundary (VM or VM-like isolation)
- A repeatable, disposable filesystem
- Strict outbound network controls
- A clean secrets story
- Forensics: who ran what, changed what, and talked to what
If you’re building AI agents that touch the outside world, sandboxing is not a “later” problem. It’s the entry price.
The 8-step checklist I use as the mental model
This is the minimal loop that keeps you out of trouble:
- Create a new VM instance (per run).
- Attach a fresh copy-on-write root overlay on top of a read-only base image.
- Configure networking on a dedicated interface (tap/bridge).
- Apply default-deny egress rules on that interface.
- Inject short-lived secrets at boot (scoped to this run).
- Run the agent’s tool loop through a single tool-runner entrypoint.
- Export artifacts and a log bundle.
- Destroy the VM and wipe overlays.
If you do only one thing from this post, do #4. Default-deny egress changes the whole risk profile.
Why letting an agent run tools is uniquely risky
“Untrusted code execution” used to be a special event. With agents, you’ve productized it.
The failure modes in real agentic workflows are not subtle. They’re the obvious stuff we all learned to fear, except now it’s automated and fast.
- Prompt injection → tool misuse. The model gets talked into running commands it shouldn’t. If you haven’t internalized this yet, read my prompt injection post and my broader AI security.
-
Supply-chain installs. The agent
pip installs ornpm installs something sketchy because it “fixed the build.” If you’re thinking “we pin versions,” congrats. That’s step 0, not the solution. -
Credential theft via environment/process. Agents and tools love environment variables. Malware loves them more.
/procvisibility and sloppy secret injection are how you lose. - Data exfiltration via outbound HTTP. If the agent can talk to the internet, it can leak. The easiest exfil path is the one you already gave it.
-
Accidental destruction. It’s not always malicious.
rm -rf, recursive edits, or “clean up this directory” on the wrong mount happens.
If your agent has a shell, it’s a junior engineer with root and zero judgment. Sandbox accordingly.
A concrete number to make this feel less hand-wavy: Firecracker’s whole pitch is density and speed because this pattern is meant to run at scale. The project site says it can start user space in as little as 125 ms, create up to 150 microVMs per second per host, and add <5 MiB memory overhead per microVM. That’s the runtime telling you “disposable per-task VMs are not crazy.”
(Those numbers are from the official Firecracker site.)
Isolation runtimes: pick your poison
There’s no perfect isolation. There are only tradeoffs you understand and can operate.
For solo devs and small teams, I keep the shortlist simple:
- Linux containers (runc)
- gVisor
- Kata Containers
- Firecracker microVMs
- Full-fat VMs (QEMU, VMware, etc.)
And yes, Kubernetes can orchestrate some of this. But you asked for “no Kubernetes required,” and I agree with the premise. K8s is great at scheduling. It does not magically solve default-deny egress, secrets lifecycle, or audit bundles. You still have to do the hard parts.
The Comparison Table
Here’s the table I wish existed back when everyone first told me “just run the agent in Docker”:
| Runtime | Isolation boundary | Cold start | Egress control ergonomics | Snapshot/rollback ergonomics | Best fit for agent sandboxes |
|---|---|---|---|---|---|
| Containers (runc) | Shared kernel | Very fast | Easy-ish (netns/iptables), but mistakes leak | Layered FS, but state leaks through mounts | Lowest friction, highest foot-guns |
| gVisor | User-space kernel layer | Fast | Similar to containers, extra guardrails | Similar to containers | Better than runc when you can accept compat gaps |
| Kata Containers | VM-backed containers | Slower than runc | VM networking patterns | VM disk patterns | When you want “container UX, VM boundary” |
| Firecracker | MicroVM (KVM) | Fast for VMs (125 ms claim) | Clean per-VM interface | Great with overlays/snapshots | Strong default for server-side “one run, one VM” |
| Full VM (QEMU) | VM (KVM optional) | Typically slower | Fine, but heavier | Fine, but heavier | When you need maximum compatibility |
If your threat model includes “agent might run arbitrary code from the internet,” VM-backed isolation is the boring answer that’s actually right.
Also: don’t over-rotate on “most secure.” The real question is “most secure that you can operate consistently.” A flaky security control is just a future incident with better marketing.
Security: default deny is the only way
Allowlisting outbound traffic feels annoying until you’re the person explaining to your cofounder why a package install beaconed to a random domain.
Default-deny egress works because it doesn’t care why the agent is misbehaving:
- prompt injection
- malicious dependency
- accidental command
- model bug
If it can’t phone home, it can’t exfiltrate.
A practical egress model for small teams
You don’t need a service mesh or a policy engine to get 80% of the value.
On a single host running disposable VMs, the pattern is:
- Give each VM a dedicated network interface (a
tapdevice, typically attached to a bridge). - Apply firewall rules on that interface (
nftableson Linux,pfon macOS,iptablesif you’re stuck in the past). - Start with deny all, then add an allowlist.
- Log denies. Deny logs are your best “what did the agent try to do?” signal.
A concrete implementation hint: I’ve seen small teams bind VM traffic to a dedicated bridge (e.g., br-agent) and apply egress rules only on that bridge. That way you’re not playing whack-a-mole with the host’s global networking.
What to allowlist (and how not to get tricked)
Most coding agents need less network than people assume. Typical allowlist buckets:
-
Git hosting:
github.com(and your internal Git host) -
Package registries:
pypi.org,files.pythonhosted.org,registry.npmjs.org, distro mirrors - Container registries: your specific registry domains if you build images
- Time + identity: an NTP source, your IdP endpoints if you’re doing OIDC
The trap: DNS is an exfil channel. If you allow arbitrary DNS to arbitrary resolvers, you’re letting the agent encode secrets in queries. Treat DNS as part of egress.
The safer approach for a small setup:
- Use a single resolver you control (even a local caching resolver).
- Pin or restrict DNS egress to that resolver.
- Allowlist by domain + resolved IP ranges, not “anything on 443.”
If you want the short version: allowlist destinations, not ports.
Disposable filesystems: snapshot, run, roll back
Agents are messy. They create files. They install packages. They “just try something.” That’s the whole value prop.
Your filesystem design should assume:
- Every run will leave garbage
- Some runs will try malware-style persistence
- You’ll eventually need to answer “what changed?”
The base image + overlay pattern
The simplest durable design is:
- A read-only base image you patch and update deliberately (weekly is fine).
- A copy-on-write overlay (per run) that captures all changes.
- A scratch/work volume (optional) for larger temp files.
On the VM side, the concept maps cleanly to qcow2 backing files or overlayfs-like semantics depending on your stack. The important part is operational: every run starts from the same base, and the overlay dies with the run.
A number to keep you honest: if you run 20 agent tasks a day and each leaves behind 2 GB of junk, you’re burning 40 GB/day. Disposable overlays make cleanup deterministic.
What should be persisted vs discarded after each agent run?
Persisting the wrong stuff is how sandboxes quietly become “semi-trusted pet environments.” That’s when weird, unreproducible issues show up. Then people blame the model. It was the state.
My rule:
-
Persist:
- Build artifacts you intentionally export (binaries, patches, generated docs)
- A structured audit bundle (more on that below)
- A minimal “run manifest” (inputs, tool permissions, allowlist config, VM image hash)
-
Discard:
- The VM disk overlay
- Package caches (
pip,npm,apt) unless you can isolate them safely - Shell history inside the VM (you already have external transcripts)
- Any copied workspace that contains secrets
Caching is the one everyone tries to sneak back in for speed. If you want warm performance without persistent state, use a warm pool of pre-booted VMs with empty overlays, not long-lived disks.
Secrets injection without leaving landmines
If your agent can access production credentials, you’ve built a very expensive secret-leaking machine.
The goal is not “the agent can deploy.” The goal is “the agent can deploy in a narrow, revocable way.”
The least-bad secrets lifecycle
For small teams, this pattern holds up:
- Mint a short-lived token per run (minutes, not days).
- Scope it to the minimum set of actions (read-only if you can).
- Inject it at boot using a user-data style mechanism (cloud-init-like), or a one-shot secrets file mounted in memory.
- Redact secrets in logs at the boundary (tool runner) before anything gets shipped.
Concrete examples of “scoped to minimum”:
- A Git token that can only read a single repo.
- A package registry token that can only download, not publish.
- A cloud token that can only write to one bucket prefix for artifacts.
And please stop putting long-lived secrets in environment variables if you can avoid it. Processes leak env. Debug logs leak env. People paste env into tickets.
If you’re doing AI in production work, secrets hygiene is where “prototype” turns into “adult supervision.”
Auditability: make every run reviewable after the fact
A sandbox that can’t be audited is security theater.
Assume you will eventually need to answer these four questions:
- What commands did the agent run?
- What network destinations did it try to reach?
- What files did it change?
- What artifacts did it produce?
If you can’t answer those quickly, you don’t have control. You have vibes.
“Wrap tool entrypoints” means one choke point
Instead of letting the agent call bash, git, pip, and curl directly, route everything through a single “tool runner” entrypoint. This is where you:
- log argv + working directory
- capture stdout/stderr
- record exit code + runtime duration
- attach a permission context (“read-only repo”, “network allowlist v3”, “no write outside /workspace”)
You can structure logs as OpenTelemetry spans if you want to get fancy. I wrote a full schema for this in AI agents.
What to log (minimum viable forensics)
Per run, I want a bundle that contains:
- Run manifest: timestamp, VM image hash, agent version, tool policy version, egress allowlist version
- Command transcript: every tool call, args, cwd, exit code
-
Filesystem diff summary: list of files created/modified/deleted under
/workspace - Network flow log: destination IP:port, SNI/hostname if available, bytes sent/received, allow/deny decision
- Artifacts: patch files, build outputs, test reports
That’s enough to reconstruct intent without saving the entire VM disk.
A concrete retention guideline that won’t bankrupt you: keep audit bundles for 30 days by default, and keep “suspicious runs” for 180 days. If you don’t have a security team, your future self is the security team.
Getting started: a no-Kubernetes architecture that actually works
Here’s the prescriptive design I’d ship for a solo dev or a small team on a single dev server.
Architecture: one host, one orchestrator, many disposable VMs
Components:
-
Agent orchestrator (a small service or even a CLI) that:
- creates a VM per run
- attaches overlay disks
- configures networking
- injects secrets
- starts the agent tool loop
- exports artifacts + logs
- destroys the VM
- MicroVM runtime: Firecracker if you’re on Linux and want density; otherwise a standard VM stack.
- Developer VM wrapper (laptop ergonomics): Lima is a pragmatic choice on macOS/Linux because it launches Linux VMs with automatic file sharing and port forwarding (similar to WSL2).
Firecracker’s own description is clear: it’s purpose-built for “secure, multi-tenant container and function-based services,” implemented as a KVM-based VMM with a minimal device model to reduce attack surface. That’s exactly the shape we want for “agent runs arbitrary tool code.”
Warm pools without Kubernetes
Competitor posts love warm pools implemented with CRDs. You don’t need that.
A warm pool for small teams is:
- Keep N pre-booted VMs paused/idle (N is usually 2–10).
- Each VM is sitting on the same base image but with an empty overlay.
- When a run starts, you assign it a warm VM, attach a fresh overlay, apply policy, and go.
You should also set:
- a hard concurrency limit (start with 2 if you’re on a laptop)
- CPU/memory caps per VM (e.g., 2 vCPU, 4–8 GB RAM per run)
- a wall-clock timeout per run (e.g., 10–20 minutes)
This is less about cost and more about blast radius. Unlimited concurrency is how an agent turns a small bug into a host meltdown.
When you actually should use Kubernetes
If you’re already operating Kubernetes well, it can help with scheduling, packaging, and lifecycle. The industry trend is real. The Kubernetes SIGs project agent-sandbox literally describes itself as enabling management of “isolated, stateful, singleton workloads” for “AI agent runtimes.”
But K8s doesn’t remove the need for:
- thoughtful default-deny egress
- secrets scoping
- auditable tool boundaries
- snapshot rollback patterns
If you don’t have those, you just have a compromised agent… scheduled nicely.
Inline illustration suggestion: “Single-host” architecture diagram with: base image store, overlay store, egress firewall, secrets broker, artifact store, log store.
A reality check (because nothing is perfect)
This approach isn’t free. It’s just the best trade I’ve found for the “agents with real tools” era.
Here are the honest limitations:
- You’re still trusting the host. VM isolation reduces guest-to-host breakout risk, but it doesn’t eliminate it. Patch your kernel. Use hardware virtualization. Reduce host attack surface.
- Egress allowlists are operational work. Registries change IPs. CDNs are annoying. If you allowlist too broadly, you lose the point. If you allowlist too narrowly, your agent can’t do its job.
- Audit logs can leak secrets. If you don’t redact at the boundary, you’ll end up storing credentials in logs. That’s worse than not logging.
- Performance and UX tradeoffs are real. Starting a VM, attaching disks, applying firewall rules. It’s extra latency. Firecracker’s design exists because people wanted VM boundaries without VM pain, but there’s still overhead.
One more: if your agent needs to interact with a user’s real browser session or OS GUI, a Linux VM sandbox helps, but it doesn’t solve the “human session is the crown jewels” problem. That’s a different architecture.
A pragmatic posture for 2026
My bias is that more teams will ship agents with tool access before they ship proper security controls. The market rewards speed. Incidents punish you later.
Running this blog’s 7-agent publishing pipeline (261+ posts), I’ve learned that deterministic gates catch an entire class of failures that “just use a smarter model” will never reliably catch. Sandboxing is the same kind of boring engineering. It’s not about smarter agents. It’s about guardrails that don’t get confused.
If you want adjacent reading on operationalizing agent systems, start with agent orchestration, AI security, and AI in production.
The point nobody wants to say out loud
Most “agent safety” conversations are still stuck on model behavior. That’s the wrong layer.
Tool-using agents are systems. Systems fail. Systems get attacked. And when they do, the only thing that matters is blast radius.
My prediction: by the time we hit 2027, “agent runs tools on the host” will be viewed the same way we now view “production app runs as root.” It’ll still exist, but it’ll be a red flag.
If you’re building agents today, you have a chance to make disposable Linux VM sandboxes the default. Not because it’s trendy. Because it’s the first design that lets you sleep.
Originally published on kunalganglani.com
Top comments (0)