The Sandbox Problem for AI Agents: Lambda MicroVMs and Where Untrusted Code Should Run
An agent writes code you never reviewed and then runs it. The interesting question isn't the prompt — it's the blast radius. Lambda MicroVMs give each session a Firecracker VM that resumes from a snapshot in under a second, including a pattern where Anthropic runs the agent loop and you run the tool calls.

Every conversation about AI agents eventually reaches the same fork, and most of them take the wrong branch. People argue about the reasoning: which model, which prompt, how many agents, what topology. Fair enough — I’ve spent a post or two there myself. But there’s a quieter question that decides whether the thing is safe to ship at all: when the agent writes a shell command or a Python script, where does that actually run?
Because it does run. That’s the whole point of a tool-using agent. It reasons, decides to rm something or pip install something or hit an internal API, and then some process on some box carries it out. The model produced code you didn’t write, didn’t review, and can’t fully predict. Where that code lands is the blast radius. Everything else is decoration.
For a while the answers were all bad in different ways.
The three bad options#
If you’ve had to run other people’s code before — CI runners, notebook backends, plugin sandboxes — you know the shape of this. You get to pick two of three: isolation, speed, and not-building-it-yourself.
- A full VM gives you a hard boundary. Separate kernel, separate everything. It also takes a minute or two to boot, which is fine for a batch job and useless for an interactive session with a human waiting on the other end.
- A container starts in a second or two, but it’s sharing a kernel with whatever else is on the host. To run genuinely untrusted code in one, you end up bolting on seccomp, gVisor, user namespaces — a pile of hardening you now own and have to keep current.
- Functions-as-a-service are quick and isolated enough, but they’re built for request-response. They don’t hold state between calls, and they time out long before a coding session with a person thinking on the other end would.
So the real choices were: accept a tradeoff, or go build virtualization infrastructure yourself. The second one is a genuine project — it quietly eats the engineers you wanted pointed at your actual product.
What Lambda MicroVMs is#
AWS’s move here is to expose the thing that was already underneath Lambda. Every Lambda function has run on Firecracker microVMs for years — trillions of invocations a month on that stack. Lambda MicroVMs just hands you the primitive directly, per session, without the request-response straitjacket wrapped around it.
The mental model is image, then launch. You package your environment — a Dockerfile plus your code — and AWS builds it once: runs the Dockerfile, starts your app, and takes a Firecracker snapshot of the running memory and disk. Every session after that resumes from the snapshot instead of cold-booting. That’s the trick that makes it feel instant. You’re not booting a VM, you’re thawing one that was already up.
A quick example. Say the environment is a small Python service:
FROM public.ecr.aws/lambda/microvms:al2023-minimalRUN dnf install -y python3 python3-pip && dnf clean allWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txtCOPY app.py .EXPOSE 5000CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]You zip that with your code, push it to S3, and aws lambda-microvms create-microvm-image builds and snapshots it once. After that, each session is a single launch with a lifecycle policy attached:
aws lambda-microvms run-microvm \ --image-identifier arn:aws:lambda:<region>:<acct>:microvm-image:my-agent-sandbox \ --execution-role-arn arn:aws:iam::<acct>:role/MicroVMExecutionRole \ --idle-policy '{"maxIdleDurationSeconds":900,"suspendedDurationSeconds":300,"autoResumeEnabled":true}'You get back an ID and a dedicated HTTPS endpoint with your app already running. Send it traffic with a short-lived token in an X-aws-proxy-auth header. Let it sit idle past the threshold and it suspends — memory and disk snapshotted, billing basically stops — and the next request thaws it with state intact. From the client’s side, nothing happened. That’s the part that matters for an agent: the installed packages, the loaded model, the files the session was halfway through editing all survive the nap. Up to eight hours total per microVM, ARM64, up to 16 vCPUs and 32 GB.
None of these pieces is revolutionary on its own. Firecracker snapshots aren’t new, and plenty of teams have hand-wired suspend/resume before. What’s new is not having to operate any of it.
The part I actually care about: the brain runs somewhere else#
Here’s the bit that made me stop and read twice. AWS documented a specific integration — Lambda MicroVMs as a self-hosted sandbox for Claude Managed Agents — and the split it draws is exactly the one I’d want.
Anthropic hosts the agent loop and the model. The reasoning — expensive, fast-moving, someone-else’s-problem to operate — stays on their side. The tool calls, the part that touches files and runs commands and reaches into your systems, run inside a microVM in your account. Anthropic has the brain; you own the hands.
Concretely, per Claude session:
- A session starts and Anthropic fires a
session.status_run_startedwebhook at an endpoint in your account. - A small launcher Lambda verifies the webhook signature and calls
RunMicroVM. - Code inside the microVM claims the session and runs the agent’s tool calls —
bash,read,write,edit,glob,grep— against a/workspacedirectory, posting results back to Anthropic. - When the session ends, the microVM is terminated. Sessions never share state.
Two details make this more than a nice diagram. First, you control the environment end to end: what’s installed, what the thing can reach on the network, which internal database or cache it’s allowed to touch (you attach a VPC egress connector at launch for the private stuff). The sensitive files and services stay on infrastructure you run, not a vendor’s.
Second — and this is the one I’d flag in a design review — your Anthropic organization API key never lands on that compute. The launcher passes a reference to a Secrets Manager secret; the microVM’s execution role reads a scoped environment key from Secrets Manager at runtime, using short-lived IAM credentials handed out through IMDSv2. If you’ve read anything I’ve written about scoping an agent’s permissions to what it’s earned, you’ll see why the ordering matters. The isolation boundary and the credential boundary line up, instead of the usual arrangement where a long-lived secret sits in an env var one prompt injection away from walking out the door.
One config note that tells you this was built for the job and not retrofitted: for per-session agents the docs have you run with suspendedDurationSeconds: 0 and autoResumeEnabled: false — no auto-resume, terminate when the session’s done — and set maximumDurationInSeconds as a hard ceiling so a stuck agent can’t quietly burn the full eight hours.
{ "maxIdleDurationSeconds": 120, "suspendedDurationSeconds": 0, "autoResumeEnabled": false}What I’d want to know before trusting it#
I haven’t run this in anger yet, so treat the following as the questions I’d ask, not verdicts.
The snapshot model is the obvious sharp edge. Because every session resumes from one pre-initialized image, anything your app does once at startup — open a connection, pull a fresh token, generate a nonce — gets frozen into the snapshot and handed to every session identically. AWS calls this out and gives you lifecycle hooks to re-run that work on resume, but it’s the kind of thing that works fine in the demo and bites you three weeks later. Anything that has to be unique per session belongs after the thaw, not during the build.
The rest is the usual new-service checklist. ARM64 only, and just a few regions at launch (US, Ireland, Tokyo). Eight hours is a real ceiling — plenty for a coding session, not for a long-lived worker. And it’s a genuinely separate API surface from Lambda functions; this isn’t a new trigger on the thing you already run, it’s a different resource with its own verbs. Functions are still the right tool for your event-driven glue. MicroVMs are for the narrow job of handing a session its own isolated box.
The takeaway#
The interesting question about agents was never really “how clever is the orchestration.” It’s “what can this thing touch, and how contained is it when it inevitably does something you didn’t foresee.” I keep circling that — it’s why I wrote about picking the least machinery that does the job and about binding permissions to evidence instead of a hostname.
Lambda MicroVMs answers the containment half, and the Claude Managed Agents integration is the cleanest packaging of it I’ve seen: the model reasons on the vendor’s side, the code runs on yours, the credentials are scoped and short-lived, and the whole environment is torn down when the session ends. Roughly the architecture I’d have sketched on a whiteboard anyway — it’s just nice to not have to build the plumbing under it myself.
I’m a platform/SRE engineer writing about making agentic AI reliable in production. If you’re putting agent-generated code anywhere near systems you care about and want a second pair of eyes on the isolation model, get in touch.