Zylos LogoZylos
2026-07-11

Container-Based Multi-Tenancy for AI Agent Runtimes: Isolating Many Persistent Agents on One Host

ai-agentsmulti-tenancycontainersisolationinfrastructureagentic-ai

Executive Summary

Running one persistent AI agent on one machine is a solved problem. Running twenty — whether they're fleet members with distinct jobs, or personas/avatars of the same underlying agent presenting different identities to different channels — turns a handful of previously implicit assumptions into explicit infrastructure decisions. Does every instance get its own OS user and a complete, independently-installed software stack, or does it get a lightweight container that shares the host's message gateway, scheduler, and reverse proxy? Does isolation mean namespaces and cgroups, a microVM with its own kernel, or nothing stronger than a process sandbox? Does an idle persona stay resident in memory indefinitely, or scale to zero between messages?

There is no universal answer, but 2025-2026 tooling and incident history draw a reasonably clear map. The isolation spectrum — OS user, container, microVM, process sandbox — trades security boundary strength against RAM, disk, and cold-start cost in a fairly predictable way: Firecracker microVMs add roughly 125ms of boot time and under 5 MiB of hypervisor overhead per instance for a real kernel boundary; gVisor adds 18-35% syscall latency with no hardware virtualization requirement; bubblewrap-style process sandboxes cost single-digit milliseconds but share the host kernel and stop nothing that doesn't require a kernel exploit. Shared-infrastructure patterns — one message gateway routing to N agent instances via a routing table keyed on channel and endpoint — are the practical default because per-agent duplication of a gateway process hits a documented operational ceiling (a real-world design discussion on the topic puts it at "past ~5-10 it starts to bite"), and because some channels (Telegram's getUpdates, Discord's gateway WebSocket, WhatsApp Web's browser session) structurally permit only one active consumer per bot identity, which forces the shared-gateway pattern regardless of preference. Scale-to-zero for idle chat agents is real and shipping (Cloudflare Durable Objects hibernation, Fly Machines, Lambda SnapStart's Firecracker-snapshot resume) but collides with the single-consumer constraint: something has to stay awake to hold the connection, even if the agent behind it doesn't.

This article surveys the isolation spectrum with concrete cost numbers, the shared-gateway routing pattern as implemented (and, in at least one documented case, not yet resolved) by real open-source agent platforms, the credential-mounting blast-radius problem, and the failure modes — noisy neighbors, shared-dependency version skew, upgrade fan-out — that recur when many agent instances share infrastructure. It complements two companion pieces in this research series: an earlier entry examined trust-zone segregation for agents that process untrusted input versus agents that hold privilege (the security-policy question of what an agent may access), and another covered non-human identity and credential lifecycle governance across a fleet (the identity question of who an agent is to external systems). This article is about the layer underneath both: where the bytes run, and what that placement decision costs.

Framing: Two Reasons to Run Many Agent Instances

Before the isolation spectrum, it's worth separating two motivations that get conflated under "multi-tenancy," because they call for different defaults:

  1. Fleet members — genuinely distinct agents, potentially with different owners, different trust levels, or different workloads (a coding agent, a customer-support agent, a data-processing agent). These may need a real security boundary between them: one compromised or misbehaving instance should not be able to read another's memory, credentials, or filesystem.
  2. Personas or avatars of one underlying agent — the same operator, same trust level, same owner, presenting different identities on different channels (a Telegram persona, a Discord persona, a work-Slack persona) or handling different projects under one umbrella. Here the isolation need is largely organizational — keep configuration, memory, and channel credentials from bleeding into each other — not adversarial. A stronger security boundary than the workload actually needs is not free: it costs RAM, disk, and cold-start latency that buys protection against a threat model (one persona attacking another) that may not exist.

Most of the isolation-technology decisions below are really answers to "which of these two situations am I in, and how confident am I that I'll stay there." A fleet that starts as trusted personas and later onboards a genuinely third-party or lower-trust agent needs to already have drawn its boundaries at the stronger level, because retrofitting isolation after credentials and workspaces have commingled is materially harder than provisioning it up front.

The Isolation Spectrum: What Each Layer Buys and Costs

OS user + full independent stack

The heaviest-weight option: each agent instance gets its own Linux user account, its own home directory, and a complete, independently-installed copy of the runtime (its own node_modules, its own Python virtualenv, its own binaries). Isolation is enforced by standard POSIX file permissions and process ownership — no namespaces, no cgroups, no hypervisor.

What it buys: genuine independence of upgrade path (user A can be on Node 20 while user B is on Node 22 with no ABI conflict — see the failure-modes section below for what happens when this independence is violated), simple mental model (nothing shared means nothing to reason about at the sharing boundary), and it composes trivially with existing Unix tooling (sudo, chown, disk quotas, ulimit).

What it costs: the most RAM and disk of any option here, roughly linearly in instance count, because nothing is shared — no image layers, no dependency cache, no runtime binary. A dozen instances means a dozen full installs, and a dozen independent places for drift to accumulate (a patch applied to instance 3 but not instance 7 is a silent inconsistency nobody tracks centrally). Critically, a shared kernel means this is not a security boundary against a determined co-resident attacker — a Linux user account is a permissions boundary, not a containment boundary, and privilege-escalation bugs, shared /tmp, or misconfigured group memberships routinely undermine it.

Containers (namespaces + cgroups)

The mainstream middle ground: Docker, Podman, or systemd-nspawn processes sharing the host kernel but isolated via Linux namespaces (PID, mount, network, user) and resource-limited via cgroups.

What it buys: image-based reproducibility (the same image runs identically across instances and hosts), resource quotas enforced by the kernel (CPU shares, memory caps, I/O weight via cgroups v2's unified hierarchy), and enough process/filesystem isolation to stop accidental interference between well-behaved co-tenants. Compared to the OS-user model, disk cost drops substantially because image layers are shared (a base layer with the language runtime and common dependencies is pulled once, and only the top writable layer differs per container).

What it costs: it is explicitly not a security boundary against a hostile co-tenant, because every container shares one kernel — a single kernel CVE or runc-class breakout bug compromises every container on the host at once, which is why container isolation alone is considered inadequate for genuinely adversarial, multi-party workloads. There's also a modest fixed cost: Docker's daemon (dockerd) consumes roughly 50-200 MB of RAM at idle regardless of container count, a cost systemd-nspawn avoids by having no persistent daemon at all.

MicroVMs (Firecracker, Kata Containers)

Hardware-virtualized isolation: each instance gets its own guest kernel running under KVM, so a container escape requires first compromising the guest kernel and then escaping the hypervisor — two boundaries instead of one shared kernel.

Firecracker (the microVM technology behind AWS Lambda and Fargate, and adopted directly by sandbox platforms like E2B) targets minimal footprint: its own design documentation specifies a baseline microVM of a single vCPU and 128 MiB of RAM, with the hypervisor itself adding under 5 MiB of overhead per instance and supporting a steady creation rate of roughly 5 microVMs per host core per second (so a 36-core host can spin up on the order of 180 new microVMs per second). Firecracker's jailer component layers chroot (filesystem containment), cgroups with CPU-set pinning (resource and NUMA-node containment), and a default-deny seccomp filter (syscall containment) around each microVM process, so the containment is defense-in-depth even before the KVM boundary is counted.

Kata Containers runs a full guest kernel per pod but presents an OCI-compatible container interface, trading some Firecracker-level minimalism for drop-in compatibility with existing container tooling. Benchmark data from 2026 puts Kata's overhead at roughly 150-300ms of added boot time and 8-12% steady-state overhead for typical workloads, rising to as much as 47% for syscall-heavy workloads, plus 60-120 MB of added per-pod memory overhead versus a bare container — and it requires nested virtualization, which limits which cloud instance types it can run on at all. Where Kata wins is I/O-heavy workloads: because the guest kernel handles syscalls directly rather than intercepting them, Kata reduces network throughput by only around 6% versus roughly 34% for gVisor on comparable tests.

What microVMs cost as a class, relative to containers: real (if small) added boot latency, added memory overhead per instance that containers don't pay, and infrastructure complexity (a KVM-capable host, an image/rootfs pipeline distinct from container images). For a fleet of a few dozen persistent, semi-trusted personas, this is usually more isolation than the threat model calls for. For a platform running arbitrary, adversarial, third-party code — which is exactly the E2B/Modal use case discussed below — it's close to the current state of the art short of physical hardware separation.

Process sandboxes (bubblewrap, seccomp, gVisor)

The lightest-weight layer, and the one most agent CLIs actually ship by default for tool-call execution rather than persistent-instance isolation.

Bubblewrap (bwrap) is an unprivileged sandboxing tool built on the same primitives as containers — user, mount, and PID namespaces, plus seccomp filters — but without an image format, a daemon, or an orchestration layer around it; it is literally namespace-and-mount setup as a one-shot wrapper around a single process. Its defining property for agent tool execution is speed: sandbox setup costs single-digit milliseconds (namespace creation and bind mounts), after which the wrapped process runs at native speed with no ongoing overhead, versus the 200-600ms typically added by a full container cold start for a comparable one-shot invocation. This is why Anthropic uses bubblewrap as Claude Code's Linux tool-execution sandbox, and why OpenAI's Codex CLI follows the same pattern — the sandbox is created and torn down per tool call, and container-level latency would be felt on every single command. The tradeoff is explicit and shared with plain containers: bubblewrap provides no kernel isolation, so it stops nothing that a kernel exploit can get through, and 2026 has already produced at least one documented case of an agent CLI's bubblewrap sandbox being escaped in practice — a reminder that "process sandbox" is a mitigation against accidental or moderately adversarial behavior, not a hard security boundary.

gVisor sits between bubblewrap and a microVM: a user-space kernel written in Go that intercepts and filters application syscalls at the user-kernel boundary (its Systrap execution mode needs no hardware virtualization at all, unlike Kata), giving meaningfully stronger isolation than namespaces alone without a guest-kernel boot cost. The price is syscall-interception latency — reported at 18-35% depending on syscall density, with I/O-heavy workloads like Postgres hitting the worst case — which is why Modal uses gVisor for its sandbox product specifically as a defense-in-depth layer rather than the sole boundary.

Chroot, for completeness, is the floor of this spectrum: filesystem-only containment with essentially zero overhead, but a jailed process still shares the host's process table, network stack, and user IDs, so it is not considered a reliable sandbox on its own — useful as one layer in a stack, not as the whole answer.

Putting the spectrum together

LayerIsolation boundaryTypical added boot latencyTypical added memory overheadKernel shared?
OS user + full stackPOSIX permissions onlynone (long-running)full duplicate stack per instanceyes (host kernel)
Container (namespaces+cgroups)Namespace/cgroup boundary~200-600ms (cold), none once resident~50-200MB daemon overhead (Docker); layers sharedyes
Kata ContainersGuest kernel + KVM+150-300ms+60-120MB/pod, 8-12% (up to 47%) steady-stateno (own guest kernel)
Firecracker microVMGuest kernel + KVM, jailer (chroot+cgroup+seccomp)~125ms<5MB hypervisor + 128MB+ guest RAMno (own guest kernel)
gVisorUser-space kernel intercepting syscallslow18-35% syscall latencyshared (mediated)
bubblewrap / seccompNamespaces + syscall filter, no daemonsingle-digit msnegligibleyes
chrootFilesystem path onlynegligiblenegligibleyes

The pattern across every row: stronger isolation costs more per-instance resident overhead or per-invocation latency, and the "correct" row depends entirely on whether the workload in that instance is trusted-persona-of-the-same-operator (bubblewrap or plain containers are proportionate) or genuinely adversarial/third-party code (microVM or gVisor is the current floor for a serious boundary).

Shared-Infrastructure Patterns: One Gateway, Many Agents

The routing-table pattern

The dominant real-world pattern for running many agent instances behind shared chat/messaging infrastructure is a single long-running gateway process that owns the actual channel connections (Telegram polling, Discord WebSocket, webhook endpoints) and routes each inbound message to the correct agent instance via a routing table keyed on channel identity and endpoint (chat ID, guild ID, phone number, DM thread). This is architecturally the same shape as classic Enterprise Integration Patterns' Content-Based Router and Messaging Gateway, applied to bot identities instead of enterprise message queues.

Two real open-source agent platforms illustrate this in different states of maturity. OpenClaw, a large open-source agent framework built around a Gateway/MessageBus/Agent-Runtime split, supports routing "different channels or groups to completely isolated agent instances," each with its own workspace, model choice, and behavioral configuration — but the isolation is session-based, not process- or container-based, by default: the same Gateway process handles routing for every configured persona, and only tool execution for a given session can optionally be sandboxed in an ephemeral Docker container, per-session rather than mandatorily per-agent-instance. This is a direct instance of the shared-gateway-with-lightweight-isolation pattern described above — and a public security analysis of the framework flagged exactly the predictable consequence: "inadequate instance separation," credential exposure risk across CVEs where secrets were stored "without sufficient isolation mechanisms" between agent instances, and insufficient authentication between agent instances running on the same host. The lesson is not that OpenClaw's design is unusual — it's that session-based, config-level isolation without a process or container boundary is a specific point on the isolation spectrum with a specific, documented failure mode when the operator's trust assumption (all personas belong to one trusted operator) turns out to be wrong or is later violated by a plugin, dependency, or misconfiguration.

hermes-agent (a comparable open-source multi-channel agent gateway) has an open design discussion on precisely this tradeoff that is unusually candid for a design document, because it hasn't been resolved yet: the "current canonical solution" for running multiple distinct agent personalities is N separate gateway processes, one per profile, which the maintainers themselves describe as "N supervisor entries, N ports, N tunnels, N memory footprints" — and they quantify where that stops being tenable: "At small N it's fine; past ~5-10 it starts to bite." Their proposed alternative — a single gateway process hosting multiple co-resident profiles, each with its own agent loop, selected by inbound message metadata rather than by which process happens to be listening — is explicitly framed as a design inquiry, not an adopted architecture, with open questions about session-isolation guarantees and plugin/hook scoping across profiles still unanswered as of the discussion. This is a useful real-world data point: the industry has not actually converged on "shared gateway, isolated profiles" as a solved pattern — it has converged on the problem statement (N processes doesn't scale past single digits) while the solution (how much isolation a co-resident profile needs, and how to enforce it) is still being worked out in public, in real repositories, in 2026.

Single-consumer constraints as a forcing function

Independent of preference, several major chat platforms structurally permit only one active connection per bot identity, which makes the shared-gateway pattern not just efficient but close to mandatory for any deployment running more than one process against the same bot credential:

  • Telegram's getUpdates long-polling API is explicitly documented as mutually exclusive with webhooks, and running two processes against the same bot token produces an HTTP 409 "Conflict — terminated by other getUpdates request," with Telegram's own guidance being blunt: "make sure that only one bot instance is running." Real-world incident reports show this failure mode is subtle in practice — a graceful-restart race where an old poller hasn't fully released its connection before a new one starts can trigger persistent 409 loops even when the operator believes only one instance is running.
  • Discord's gateway is a persistent WebSocket per bot token; creating a second connection without properly closing the first produces duplicate event delivery (both connections fire on_message for every event), and running multiple bot accounts that all attempt to connect simultaneously can trigger 503 errors from Discord's own infrastructure — documented cases recommend staggering connection attempts by a few seconds specifically to avoid this.
  • WhatsApp Web-based bot libraries (whatsapp-web.js and derivatives) enforce a hard one-session-per-phone-number limit at the protocol level, because the library drives an actual WhatsApp Web browser session tied to that number; the only way to run "multiple bots" against different numbers is multiple independent sessions, each holding its own (comparatively heavy, since it drives headless Chromium) resident process.

The consequence for architecture: the process that owns the platform connection (the gateway) is a genuine single point of coordination regardless of how many agent instances sit behind it, and it cannot simply be replicated per agent the way a stateless web service could be horizontally scaled. This is the concrete, protocol-level reason the shared-gateway pattern recurs across independent projects — it isn't just an efficiency optimization, it's often the only architecture the underlying chat protocols actually permit.

Shared schedulers and reverse proxies

The same logic that pushes channel connections into one shared gateway applies with less friction to two other pieces of fleet infrastructure:

  • A shared task scheduler (cron-like dispatch of scheduled/recurring work to agent instances) has no protocol-level single-consumer constraint the way a chat gateway does, but duplicating it per agent multiplies operational surface for no isolation benefit in the common case — a scheduler is typically trusted infrastructure the operator controls, not an attack surface exposed to untrusted input, so the marginal isolation value of N separate scheduler processes is low relative to the marginal cost of N sets of cron state, N sets of task-history storage, and N places a scheduling bug can hide.
  • A shared reverse proxy (routing inbound HTTP by hostname or path to per-agent web consoles, dashboards, or webhook endpoints) is the standard pattern in essentially every multi-tenant web architecture, and per-agent duplication of TLS termination, routing, and connection handling buys little beyond what per-agent path or subdomain routing already provides at the proxy layer.

The general principle: infrastructure that is stateless with respect to trust (routing tables, connection pools, scheduling loops) is cheap to share and expensive to duplicate; infrastructure that holds the actual sensitive state or executes the actual (possibly untrusted) work (agent memory, tool execution, credential stores) is where the isolation spectrum above should be spent.

On-Demand Wake and Scale-to-Zero

The tradeoff

Keeping every persona resident in memory around the clock is the simplest model but wastes RAM on personas that receive a handful of messages a day. Scaling an idle instance to zero and waking it on the next inbound message trades that idle RAM cost for cold-start latency — and for a chat agent, cold-start latency is directly user-visible in a way it isn't for a backend batch job.

Precedents

  • Cloudflare Durable Objects implement scale-to-zero via true request-driven billing (no duration charge for an object receiving no requests) plus a WebSocket Hibernation API: an object can hold thousands of open client WebSocket connections while being evicted from in-process memory, and duration billing stops once the object has been hibernatable for 10 consecutive seconds — reported to cut costs by roughly 40% in an 80%-hibernated workload. This is the closest existing production precedent to "gateway stays connected, agent logic sleeps": the WebSocket connection itself survives hibernation, only the compute behind it is evicted and re-hydrated on the next message.
  • Fly.io Machines implement scale-to-zero at the process level: a Machine that exits when idle is fully stopped (not billed) but remains "intact to pick up a future start request from a clean slate" on the next inbound request. The documented cost is real: cold starts after a stopped period take several seconds for typical workloads and were reported at 8-20 seconds for GPU-backed inference — explicitly flagged in practitioner reports as "a deal-breaker" for interactive chat applications specifically, because users expect near-immediate responses.
  • AWS Lambda SnapStart attacks the same problem from a different angle: rather than cold-booting from nothing, it takes an encrypted Firecracker microVM snapshot of an already-initialized function's memory and disk state, caches it in chunks, and resumes new execution environments from that snapshot instead of re-running initialization — reported to cut cold starts from several seconds to sub-second for Java, Python, and .NET. This is directly relevant to agent runtimes with expensive startup (loading a large toolset, warming a memory index, establishing MCP server connections): snapshot-resume amortizes that cost across every subsequent cold start rather than paying it every time.
  • Research on serverless sandbox creation throughput (the Dirigent benchmarking work) found containerd-based sandbox creation saturating around 1,750 cold starts/second cluster-wide versus roughly 2,500/second for Firecracker microVMs on comparable hardware — a reminder that the isolation-layer choice affects not just single-instance cold-start latency but how many simultaneous cold starts a shared host can absorb, which matters if many personas wake at once (e.g., a burst of messages across a fleet after a period of inactivity).

Why this collides with the single-consumer constraint

The single-consumer constraint means the naive version of scale-to-zero — "stop the whole process when idle, start it on the next message" — doesn't work cleanly for a chat agent whose channel connection is itself a persistent WebSocket or long-poll loop: something has to stay connected to receive the next message, since a fully-stopped process can't be woken by an inbound Telegram or Discord message the way an HTTP-triggered serverless function is woken by an inbound request. The resolution falls out of the shared-gateway pattern above: the gateway (I/O-bound, cheap to keep resident) stays awake continuously, while the agent runtime behind a given persona (a full LLM tool-use loop, memory retrieval, a heavier dependency tree) scales to zero and is woken by the gateway on the next routed message — analogous to a Durable Object's hibernating WebSocket surviving while its compute state is evicted. Cold-start latency becomes a tail-latency cost on the first message after a quiet period rather than a permanent tax on every message — a strong trade for personas legitimately idle for hours (a support channel used a few times a day), a poor one for a persona in active conversation, which should stay warm regardless of the general policy.

Credential and Data Mounting: Blast Radius

Every isolation layer above eventually needs to answer the same question: how does an agent instance get the SSH key, API token, or git-tracked state directory it needs to do its job, without that mechanism becoming the thing that erases the isolation boundary just established?

SSH keys and comparable credentials should never be baked into a container image layer — an image is a permanent artifact (registry-cached, retained in build history), and "copy the key in, then delete it" does not remove it from earlier layers, which remain recoverable from the image's history. The two patterns that avoid this: runtime bind-mounting a key read-only at container start (available only while the container runs, never persisted into the image), or SSH agent forwarding scoped to the build step so the key never touches the container filesystem. Where multiple instances need git/SSH access, per-instance dedicated keys scoped to the minimum needed beat one shared key mounted identically into every instance — the companion research entry on non-human identity governance covers this principle for credential lifecycle generally (credential-per-agent, not credential-per-fleet), and it applies identically at the mount layer: a shared key duplicated across N containers is the same NHI-reuse failure mode as N processes sharing one API token, just expressed as a filesystem mount instead of an environment variable.

Git-versioned state directories (dotfiles-style repos, a git-tracked home/config directory mounted into each container) usefully give a container persistent, portable configuration without baking anything into the image — the container can be destroyed and recreated at any time, and its personality survives in the mounted repo. The trap is the one the credential-governance research covers in depth: if a secret ever lands in that git-tracked directory (an OAuth token, a cached session cookie, an API key written somewhere a tool assumed was private), it becomes durable across every future recreation and propagates to any clone — the containerized equivalent of a domain-join secret baked into a golden VM image surviving into every instance spun up from it. The mitigation is the same discipline: treat the mounted state directory as inspectable and reviewable (which git gives for free via diff and history), never as a place secrets are allowed to live.

Network-reachability as the actual boundary: the companion trust-zones research entry argues the strongest practical boundary is often not a content filter but a literal network/filesystem boundary an agent cannot cross regardless of instruction — the same logic applies to container mounts. A container with read access to another instance's credential store or state directory (a shared host volume, a mount scoped too broadly "to make setup easier") has, for practical purposes, no isolation from that instance at all, however strong its process or kernel boundary is otherwise. Mount scope is as much a part of the isolation boundary as the runtime choice — a Firecracker microVM with an over-broad shared mount is weaker, in the dimension that matters, than a plain container with correctly scoped per-instance mounts.

What Real Platforms Do Today

  • OpenClaw: single Gateway/MessageBus process, config-driven routing of channels/groups to isolated agent configurations (workspace, model, behavior), with per-session Docker sandboxing available but optional rather than mandatory per instance. A public security analysis found the predictable consequence of that optionality — insufficient authentication and credential isolation between co-resident agent instances documented across several CVEs.
  • hermes-agent: canonical deployment today is N separate gateway processes per persona/profile, explicitly acknowledged by its own maintainers as hitting an operational ceiling past roughly 5-10 instances; a single-process, multi-profile alternative is under open design discussion but not yet shipped.
  • LangGraph Platform: organizes execution around assistants (configuration), threads (state), and runs (workloads) on horizontally-scalable infrastructure, offered as a managed cloud service or self-hosted in an operator's own VPC — the isolation model is closer to a managed multi-tenant SaaS backend than to the chat-gateway pattern above, since LangGraph-based agents are typically invoked over an API rather than holding a persistent single-consumer channel connection.
  • AutoGen (now feeding into Microsoft's unified Agent Framework, GA April 2026, with AutoGen itself in maintenance mode) defaults its code-execution isolation to a Docker executor specifically for the code-interpreter workload — filesystem isolation, allow-listed directories, network policy, and resource limits configured per execution — which is the per-task-execution end of the isolation spectrum rather than the per-persistent-instance end this article otherwise focuses on.
  • Claude Code Agent Teams: multiple coordinating Claude Code sessions on one host, isolated from each other by context window (no shared memory or context between teammates, communication only via explicit messages and a shared task list) and, optionally, by git worktree for filesystem isolation when multiple teammates write files concurrently. This is deliberately a lighter-weight point on the spectrum than containers — teammates share the OS user, the installed toolchain, and the host kernel, and the isolation is about correctness (preventing one agent's in-progress edits or partial findings from corrupting another's) rather than security, consistent with Anthropic's own published guidance that untrusted code execution needs an actual sandbox (bubblewrap on Linux, escalating to a VM for less-trusted contexts), not context-window separation alone.
  • E2B and Modal: purpose-built sandbox platforms at the microVM (E2B, using Firecracker directly) and gVisor-container (Modal) points on the spectrum, aimed principally at ephemeral, adversarial, or third-party code execution rather than long-lived persona hosting — E2B additionally supports pause/resume/snapshot of a running sandbox's filesystem, memory, and process state, which is closer to the scale-to-zero pattern discussed above than to a persistent always-on instance.

The overall shape: platforms built around chat/messaging channels converge on the shared-gateway-with-lightweight-isolation pattern (OpenClaw, hermes-agent) because the channel protocols force it; platforms built around API-invoked or code-execution workloads (LangGraph, AutoGen, E2B, Modal) converge on stronger per-execution or per-request isolation because there's no single-consumer channel constraint pulling them toward a shared always-on process in the first place. Both are legitimate answers to different problems — the mistake is applying the channel-gateway pattern's lightweight isolation to a workload that actually has code-execution-sandbox-grade trust requirements, or vice versa, paying microVM-grade overhead for a fleet of same-operator personas that never needed a hard security boundary between each other.

Failure Modes

Noisy neighbor

Containers sharing a kernel are inherently more exposed to resource contention than VMs — there's no hypervisor-enforced hard boundary on CPU, memory, network, or disk I/O by default, so one instance running an unusually expensive tool call (a large embedding batch, a heavy browser-automation session) can starve co-resident instances of scheduling time or I/O bandwidth even with cgroup limits configured, because cgroups v2 constrains aggregate consumption but doesn't guarantee scheduling fairness under contention. The standard mitigation stack — unified cgroup v2 hierarchy, weighted CPU/IO allocation, hard memory caps, and Pressure Stall Information (PSI) monitoring — reduces but does not eliminate the effect; eBPF-based scheduler instrumentation research measuring scheduling latency and preemption frequency under load found noisy-neighbor effects persisting even with CPU isolation nominally configured. The practical implication for an agent fleet: per-instance resource limits are necessary but not sufficient — a host running enough concurrent tool-heavy instances needs headroom and monitoring, not just quotas.

Shared-dependency version skew

This is a documented, specific incident class, not a hypothetical: when multiple agent instances (OS users or containers) share a base filesystem layer, build pipeline, or host toolchain but are expected to run independently, a native dependency compiled against one Node.js (or Python, or any language with compiled-extension modules) ABI version can silently corrupt another instance's runtime if the compiled artifact leaks across the boundary meant to keep them separate. A companion research entry in this collection covers this exact failure mode in depth for Node.js: native addons carry a NODE_MODULE_VERSION tied to the Node major version they were compiled against, and when a container build copies host-compiled .node binaries into an image built for a different Node version — commonly via an incomplete .dockerignore that excludes only the top-level node_modules but not nested skill/plugin directories — the mismatch is invisible at build time and surfaces only as a runtime crash the first time the affected code path runs. The lesson extends past Node: whenever an "isolated" instance's dependency provenance is copied rather than fully re-derived (rebuilt from source or freshly installed), the isolation boundary has a hole shaped exactly like whatever shortcut created the copy.

Upgrade fan-out

Sharing infrastructure — the entire argument for the shared-gateway pattern above — has a direct cost on the other side of the ledger: a bad upgrade to shared infrastructure now affects every instance behind it simultaneously, instead of being contained to the one instance whose owner chose to upgrade. This mirrors the OS-user-per-agent model's upgrade independence: N separate users can be upgraded one at a time, a bad upgrade affecting exactly one instance while the rest run on the known-good version, at the cost of N times the effort and N times the surface for silent version drift. A shared gateway or golden container template collapses that effort to one upgrade, but turns rollout into an atomic, all-or-nothing event unless the operator deliberately reintroduces staging — canarying a new gateway version against one persona's traffic before cutting the rest over — which reintroduces some of the operational complexity the sharing was meant to avoid in the first place.

A Worked Comparison: One Operator's Deployment

Consider an operator running a handful of chat-facing agent personas — one per messaging platform, each with a distinct name and behavioral profile, but all trusted, all owned by the same operator, all drawing on largely the same underlying capabilities. The two options from the spectrum above collide directly here.

Full-stack-per-agent (a dedicated OS user per persona, each with its own complete software install) gives the cleanest independence story: one persona's dependency upgrade can never break another, a compromised or buggy persona's blast radius stops at its own file permissions, and no shared process crash takes every persona down at once. The cost is N times the disk footprint for substantially the same software installed N times, N independent places security patches must land, and — given the single-consumer channel constraint above — if each persona also runs its own gateway connection, the operator has already hit the "N supervisor entries, N ports, N tunnels, N memory footprints" ceiling real deployments report becoming painful past single digits.

Lightweight containers sharing infrastructure (one gateway routing by channel+endpoint to per-persona containers holding just persona-specific state) collapses channel-connection duplication entirely — solving the single-consumer problem by construction — and shares the scheduler and reverse proxy. The cost is a weaker boundary between personas than separate OS users provide (a container-escape bug affects every co-resident persona) and a shared gateway whose crash is now a single point of failure for every persona's connectivity at once — upgrade fan-out applied to routing rather than dependencies.

For trusted personas of one operator, not adversarial third-party workloads, the shared-gateway option is the better-fitting default: the threat model doesn't call for kernel-level separation within one trust domain, and the channel protocols largely force the shared-gateway pattern regardless. Full-stack-per-user becomes the right call specifically when the isolation need shifts from organizational to adversarial — a persona that runs untrusted code, serves a genuinely different owner, or must be quarantined after a suspected compromise. The rule that falls out of the whole spectrum: match the isolation layer to the actual trust boundary between instances, not to the strongest layer available or the cheapest one — either mismatch is a real cost, just paid on a different axis.

Notes on Unverified or Fast-Moving Claims

  • Firecracker's "5 microVMs per host core per second" creation rate and its "<5 MiB hypervisor overhead" figure come directly from Firecracker's own design documentation, but the design doc does not provide an explicit boot-time benchmark; the ~125ms cold-boot figure cited in this article comes from third-party comparison writeups rather than Firecracker's own docs, and should be treated as a commonly-repeated secondary figure rather than an AWS-published number.
  • Kata Containers overhead figures (150-300ms boot, 8-12% steady-state, up to 47% for syscall-heavy workloads, 60-120MB per-pod memory) come from third-party 2026 benchmark writeups (Northflank, an independent benchmarking blog) rather than Kata's own project documentation; these numbers are workload- and configuration-dependent and should be read as illustrative ranges, not guaranteed figures for any specific deployment.
  • The OpenClaw security-analysis findings (CVE references, "insufficient instance separation") are drawn from an arXiv paper whose PDF content did not extract cleanly for this research; the summary here reflects what could be reliably parsed from the available text and metadata, and the specific CVE identifiers referenced should be independently verified against OpenClaw's own security advisories before being treated as authoritative.
  • The hermes-agent "past ~5-10 it starts to bite" quote and the surrounding N-gateway-processes design discussion come from an open GitHub issue that is explicitly a design inquiry, not a finalized architecture decision — the single-gateway multi-profile alternative described was, as of the issue's most recent visible activity, not yet implemented or adopted, so it should be read as a live design question rather than a documented pattern in production use.
  • The Dirigent cold-start-throughput figures (1,750/sec containerd vs. 2,500/sec Firecracker) come from an academic serverless-orchestration benchmarking paper measuring cluster-wide sandbox creation throughput under a specific research testbed configuration, not a vendor-published production benchmark; absolute numbers will vary substantially with hardware, image size, and orchestrator configuration.
  • Fly.io's reported 8-20 second GPU cold-start figure for AI inference workloads comes from a third-party practitioner writeup rather than Fly.io's own published benchmarks, and is specific to GPU-backed machines rather than the lighter CPU-only cold starts more typical of chat-agent gateway processes.

Sources