An AI agent debugging a failing deploy needs a shell. It runs cat on a log file, greps for a stack trace, maybe restarts a process to see if the error reproduces. That's the job. But if the shell it's running in is the same kernel, the same network namespace, or even the same node as the tenant's actual production container, one hallucinated rm -rf, one prompt-injected command, or one exploited CVE in the agent's own tooling doesn't stay contained to a scratch sandbox — it lands directly on the workload the agent was supposed to be operating.
That distinction — agent execution sandbox versus tenant deploy target — is the part the current wave of agent-sandbox products doesn't solve, because they were never built to solve it.
Why E2B's Model Doesn't Cover This
E2B, Daytona, and Modal all converged on the same shape in 2026: one ephemeral microVM per agent session, code execution isolated from the host, thrown away when the run ends. E2B wraps each sandbox in its own Firecracker microVM — a separate kernel per session, not just a separate container — which is why a container-escape CVE in one sandbox can't reach the host or a neighboring tenant's sandbox. It's a genuinely solid model, and it's why 88% of Fortune 100 companies building agentic workflows reportedly use E2B-style isolation somewhere in their stack.
But that model assumes the agent's code execution is the workload. Run the sandbox, get an answer, discard it. A platform where the agent is also the thing deploying and operating a tenant's long-running app has a second, harder problem those products were never scoped to answer: the sandbox running the agent's shell commands and the container running the tenant's actual service are two different things that need two different blast radii — and "throw away the microVM when the session ends" says nothing about how those two things should be kept apart from each other while the session is running.
This isn't hypothetical risk. Microsoft disclosed CVEs in May 2026 showing prompt injection against Semantic Kernel achieving host-level remote code execution. CVE-2025-59528 and CVE-2025-59536 are both documented sandbox-escape paths from agent tooling. The pattern in every case is the same: an agent with shell access got asked to do something adjacent to its intended job, and the isolation boundary around "adjacent" wasn't tight enough.
Three Layers of Separation
Here's the concrete architecture for keeping an agent's shell separate from the tenant workload it operates, mapped onto a Cluster API-provisioned fleet — the same primitive a git-push PaaS already uses to provision tenant node pools.
Layer 1: Separate node pools, enforced by taints
Cluster API's MachineDeployment is the resource for a group of worker nodes with a shared configuration — the standard pattern is one MachineDeployment per workload category (default, compute-heavy, GPU) rather than one large undifferentiated pool. Apply that same pattern to agent sandboxes: a dedicated MachineDeployment for agent-shell nodes, tainted via KubeadmConfigTemplate so nothing schedules there without an explicit toleration.
The toleration only gets injected into agent-sandbox pods — via a mutating admission webhook scoped to the agent-sandbox namespace — never into the tenant's own deploy pods. The result is a hard scheduling guarantee, not a convention: an agent's shell pod and the tenant container it's debugging physically cannot land on the same node, so a kernel-level escape from the agent side has no neighboring tenant workload to reach.
Layer 2: Default-deny network policy between the two namespaces
Node separation stops a co-located escape; it does nothing about the agent's pod reaching the tenant's container over the pod network if they're both still inside the cluster's flat address space. The fix is a NetworkPolicy that default-denies all traffic into and out of the agent-sandbox namespace, then explicitly allows only the one path the agent actually needs: egress to the platform's own control-plane API (the same deploy / rollback / logs endpoints a human operator or an MCP tool call would use).
That's a meaningful constraint worth stating plainly: the agent should never get a direct network path to the tenant's container. It talks to the control plane, and the control plane talks to the workload — the same indirection a REST or MCP API already enforces, just extended down to the network layer so it can't be bypassed by anything running inside the agent's own shell. One caveat from the Kubernetes docs worth taking seriously: NetworkPolicy operates at L3/L4 only — it doesn't restrict access to cluster DNS or the Kubernetes API server, so it has to be paired with RBAC scoping the agent's service account, not treated as sufficient alone.
Layer 3: Stronger runtime isolation on the agent side specifically
The agent's shell is running the least-trusted, most dynamic code on the entire platform — LLM-generated commands reacting to whatever the debug session turns up — so it's the one place where the tenant's own container runtime choice shouldn't automatically apply. Three real options, with real numbers from 2026 production deployments:
| Runtime | Isolation model | Cold start | Steady-state overhead |
|---|---|---|---|
| Standard container (runc) | Shared host kernel | <1s | ~0% |
| gVisor | Userspace syscall interception, no hardware boundary | Container-speed | 18-35% (worse for I/O-heavy workloads) |
| Kata Containers / Firecracker | Real VM, separate kernel, KVM-backed | ~600ms (Kata) / <150ms (Firecracker) | 8-12% (Kata) |
gVisor is a legitimate choice for the tenant's own workload — moderate isolation, better performance than a full VM, good enough when the code is a known application rather than an open-ended agent shell. But for adversarial multi-tenancy — code you can't fully trust and can't fully predict — Kata Containers or Firecracker provide the hardware-backed boundary gVisor's userspace interception doesn't: a kernel-exploit escape from the agent's sandbox hits a VM boundary, not the host kernel the platform's own control plane runs on.
The recommendation follows directly from the threat model: put the agent's shell on a Kata- or Firecracker-backed RuntimeClass, even if the tenant's own containers run on plain runc or gVisor for cost and cold-start reasons. The two workloads have different trust levels; there's no reason they should share a runtime.
What This Actually Costs
None of this is free, and pretending otherwise is how the isolation gets skipped under deadline pressure. Three concrete costs:
- Idle reserved capacity. A dedicated agent-sandbox node pool means capacity provisioned and sitting there even when no agent session is active — unlike a shared pool where any idle node can pick up any workload. On a small fleet this is a real per-month dollar cost, not a rounding error.
- Cold-start latency. Kata's ~600ms versus Firecracker's <150ms is the tax for the stronger isolation boundary, paid on every new agent session. For a debug shell a human is waiting on, 600ms is noticeable; for a fire-and-forget batch task, it isn't.
- Two runtime classes to operate. Running gVisor for tenant workloads and Kata/Firecracker for agent sandboxes means twice the operational surface — two VMM configurations, two sets of node images, two things that can break in an upgrade — instead of standardizing on one.
The honest threshold: this three-layer separation is worth building the moment an agent gets any shell-level access to a live debugging session — not before. A read-only logs API call an agent invokes doesn't need a dedicated node pool or a Kata runtime; it's a bounded, auditable API call with no shell attached, and the existing control-plane RBAC already scopes what it can see. The isolation architecture above is specifically for the harder case: an agent that can run arbitrary commands to diagnose a failure, which is a materially different capability than an agent that can call three predefined endpoints.
Where This Leaves an MCP-Driven Deploy Flow
That threshold is also why an MCP server built around deploy / rollback / logs as discrete tool calls sidesteps most of this problem for now: there's no agent shell in that model at all, just scoped API calls with the same auth boundary a human operator would go through. The three-layer architecture above only becomes load-bearing the day an MCP tool call evolves into "give the agent an interactive shell to debug this failing deploy" — a genuinely useful capability, and one that shouldn't ship without the node-pool, network-policy, and runtime-class separation already built and tested, not retrofitted after the first incident makes the gap obvious.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.
Sources
- E2B vs Modal: comparing AI code execution sandboxes in 2026 — Northflank
- AI Agent Sandboxing in 2026: Docker, E2B, Firecracker, gVisor, Modal & Daytona Compared — amux
- Kata, gVisor, or Firecracker? Container Isolation Guide — Edera
- Kata Containers vs gVisor — Northflank
- Breaking out: Can AI agents escape their sandboxes? — Help Net Security
- Kubernetes multi-tenancy: A 2026 guide to secure shared infrastructure — Northflank
- Multi-tenancy — Kubernetes documentation
- How to Configure Cluster API Machine Deployments with Flux — OneUptime
- Taints and Tolerations — Kubernetes documentation
All figures and CVE references above are drawn directly from the linked sources.



