Feat Req: MicroVM sandboxes on Lambda
Feature request: a component and an SDK primitive for AWS Lambda MicroVM sandboxes
The problem
We build an AI agent platform. An agent writes a script, runs it, reads the output, and then writes a better script. Each iteration needs an isolated environment with a real filesystem, controlled network access, and a lifetime of minutes to hours. The environment must keep its state between steps.
SST has no component for this shape of work.
sst.aws.Functionstops after 15 minutes and keeps no state.sst.aws.Taskstarts slowly, has no per-instance endpoint, and has no suspend.sst.aws.Serviceis a long-running fleet, not a per-session environment.
AWS shipped the correct primitive on 22 June 2026. Lambda MicroVMs are Firecracker MicroVMs on Amazon Linux 2023, generally available in us-east-1, us-east-2, us-west-2, eu-west-1, and ap-northeast-1, on ARM64 only. AWS names "AI code execution sandboxes" as the first use case in its own guide.
What a developer must write today
The SDK is a transport, not an abstraction. The caller still owns: the poll loop, the URL construction, two reserved header names, the token expiry, the retry policy for a 5 TPS RunMicrovm quota, the 502 that means auto-resume failed, and the termination on every error path. SST already removed exactly this class of work for ECS.
import time
import boto3
import requests
mv = boto3.client("lambda-microvms", region_name="eu-west-1")
# 1. Run. State starts at PENDING.
run = mv.run_microvm(
imageIdentifier="arn:aws:lambda:eu-west-1:123456789012:microvm-image:agent-sandbox",
executionRoleArn="arn:aws:iam::123456789012:role/SandboxExecutionRole",
ingressNetworkConnectors=[
"arn:aws:lambda:eu-west-1:aws:network-connector:aws-network-connector:ALL_INGRESS"
],
egressNetworkConnectors=[
"arn:aws:lambda:eu-west-1:aws:network-connector:aws-network-connector:INTERNET_EGRESS"
],
idlePolicy={
"autoResumeEnabled": True,
"maxIdleDurationSeconds": 900,
"suspendedDurationSeconds": 1800,
},
maximumDurationInSeconds=3600, # valid range 1 to 28800
runHookPayload='{"session":"abc"}', # delivered to the /run hook
)
microvm_id = run["microvmId"]
endpoint = run["endpoint"]
# 2. Poll until RUNNING. There is no waiter.
while mv.get_microvm(microvmIdentifier=microvm_id)["state"] == "PENDING":
time.sleep(0.5)
# 3. Get a JWE token. Maximum lifetime is 60 minutes. It is port scoped.
tok = mv.create_microvm_auth_token(
microvmIdentifier=microvm_id,
expirationInMinutes=30,
allowedPorts=[{"allPorts": {}}],
)
token = tok["authToken"]["X-aws-proxy-auth"]
try:
# 4. Send a request. Two reserved headers. Default target port is 8080.
r = requests.post(
f"https://{endpoint}/exec",
headers={"X-aws-proxy-auth": token, "X-aws-proxy-port": "8080"},
json={"code": "print(1 + 1)"},
timeout=60,
)
r.raise_for_status()
result = r.json()
finally:
# 5. Clean up. A leaked MicroVM bills until the 8 hour cap.
mv.terminate_microvm(microvmIdentifier=microvm_id)How SST already solves the comparable case
sst.aws.Task splits the problem in the same way.
- The component (
components/aws/task.ts) provisions the durable parts: task definition, execution role, task role, and an optional security group. getSSTLink()returnspropertiesplusinclude: [permission({ actions, resources })]. It does not return a running task.- The runtime call lives in the SDK,
sst/aws/task, astask.run(),task.describe(), andtask.stop(). - Non-Node consumers read the same linked
Resourceobject. Our FastAPI server does this inserver/services/pipeline_trigger.py: it readsResource.PipelineWorker.cluster,.taskDefinition,.subnets, and.securityGroups, then callsecs:RunTaskwith boto3.
A MicroVM component fits this split without changing it.
The proposal
1. Component: sst.aws.MicroVm
I suggest MicroVm and not Sandbox.
const bucket = new sst.aws.Bucket("Artifacts");
const sandbox = new sst.aws.MicroVm("AgentSandbox", {
// Same image args shape as Task. SST zips context + Dockerfile and
// uploads it to the bootstrap bucket instead of building and pushing.
image: { context: "./sandbox", dockerfile: "Dockerfile" },
memory: "2 GB", // 0.5 | 1 | 2 | 4 | 8; vCPU and disk follow
architecture: "arm64", // only valid value; error on x86_64
// Build-time hooks. Both are optional.
hooks: { port: 8080, ready: "/ready", validate: "/validate" },
// Defaults applied by the SDK on run(). Overridable per run.
duration: "1 hour", // maximumDurationInSeconds, max 8 hours
idle: { suspend: "15 minutes", terminate: "30 minutes", autoResume: true },
environment: { PYTHONUNBUFFERED: "1" },
link: [bucket], // grants go on the execution role
permissions: [{ actions: ["bedrock:InvokeModel"], resources: ["*"] }],
vpc, // creates a NetworkConnector + operator role
logging: { retention: "1 week" },
transform: { image: {}, buildRole: {}, executionRole: {} },
});
new sst.aws.Function("Api", { handler: "src/api.handler", link: [sandbox] });It provisions: the zip artifact and its S3 object, the build role, the execution role (carrying link and permissions), the MicrovmImage, the CloudWatch log group, and, when vpc is set, the NetworkConnector and its operator role.
getSSTLink() returns:
{
properties: {
imageArn, imageVersion, executionRoleArn,
ingressNetworkConnectors, egressNetworkConnectors,
defaults: { maximumDurationInSeconds, idlePolicy },
},
include: [
permission({
actions: [
"lambda:RunMicrovm", "lambda:GetMicrovm", "lambda:ListMicrovms",
"lambda:SuspendMicrovm", "lambda:ResumeMicrovm",
"lambda:TerminateMicrovm", "lambda:CreateMicrovmAuthToken",
],
resources: [imageArn, `arn:aws:lambda:${region}:${account}:microvm:*`],
}),
permission({ actions: ["iam:PassRole"], resources: [executionRoleArn] }),
],
}2. SDK: sst/aws/microvm
This is the analogue of task.run(). It is the part that boto3 cannot give, because boto3 has no link data and no session object.
import { Resource } from "sst";
import { microvm } from "sst/aws/microvm";
// run() waits for RUNNING and mints the first token.
const vm = await microvm.run(Resource.AgentSandbox, {
payload: { session: sessionId }, // -> runHookPayload
duration: "30 minutes",
ports: [8080], // token scope
});
// fetch() is a normal fetch. It adds the base URL, X-aws-proxy-auth,
// and X-aws-proxy-port, and refreshes the token before it expires.
const res = await vm.fetch("/exec", {
method: "POST",
body: JSON.stringify({ code }),
});
await vm.suspend(); // memory + disk checkpoint
await vm.resume();
console.log((await vm.describe()).state); // PENDING | RUNNING | SUSPENDED | ...
await vm.terminate();
// Reattach in a later request or a later process.
const same = microvm.attach(Resource.AgentSandbox, vm.id);Three things justify the SDK over raw calls: it hides the two reserved headers, it owns token refresh inside the 60-minute cap, and it retries RunMicrovm correctly against a 5 TPS quota.
3. Python and other consumers
We do not need a Python SDK from SST. We need the link data to be complete enough that the raw calls are short. With the properties above, our consumer becomes run_microvm(**Resource.AgentSandbox.defaults, imageIdentifier=..., executionRoleArn=...).
Where this strains SST's model:
sst devhas no local analogue.Tasksolves this with a stub image and the Live bridge. I do not think that is worth building first. A v1 can run the Dockerfile with local Docker and pointResourceatlocalhost, or simply not support dev mode.- Region support is 5 regions currently.
- Image builds are asynchronous and slow, and concurrent builds are capped at 5 per account per Region (10 in four Regions).
- Cost safety. A leaked MicroVM bills until the 8-hour cap. The component should require a
durationand an idle policy, and default them to short values.
Minimum viable first version:
sst.aws.MicroVmwithimage,memory,hooks,environment,link,permissions,logging,transform.- Build role, execution role, S3 artifact,
MicrovmImage, log group. getSSTLink()with the properties and permissions above.microvm.run(),.fetch(),.describe(),.terminate()insst/aws/microvm.- A plan-time error for unsupported Regions and for
x86_64.
Suspend, resume, attach(), VPC connectors, and dev mode can follow.
This feature request is intended to be a discussion forum for the community. Happy to pick this up in collaboration with a maintainer or another community member.
Sources
- Lambda MicroVMs guide: https://docs.aws.amazon.com/lambda/latest/dg/lambda-microvms-guide.html
- Create your first MicroVM: https://docs.aws.amazon.com/lambda/latest/dg/microvms-getting-started.html
- MicroVM images (sizing, base images, build hooks): https://docs.aws.amazon.com/lambda/latest/dg/microvms-images.html
- Running and using MicroVMs (run, connect, hooks, suspend/resume): https://docs.aws.amazon.com/lambda/latest/dg/microvms-launching.html
- Networking (connectors, ports,
X-aws-proxy-*, JWE): https://docs.aws.amazon.com/lambda/latest/dg/microvms-networking.html - Security and permissions (IAM actions, roles, ARNs): https://docs.aws.amazon.com/lambda/latest/dg/microvms-security.html
- Lambda quotas, "Lambda MicroVMs" section (ARM64, 400 GB / 1,024 GB, 5 concurrent builds,
RunMicrovm5 TPS): https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html - MicroVMs API reference, action list: https://docs.aws.amazon.com/lambda/latest/microvm-api/API_Operations.html
RunMicrovmrequest and response: https://docs.aws.amazon.com/lambda/latest/microvm-api/API_RunMicrovm.html- Launch announcement, 22 June 2026 (GA, Regions): https://aws.amazon.com/blogs/aws/run-isolated-sandboxes-with-full-lifecycle-control-aws-lambda-introduces-microvms/
- Compute blog, technical detail: https://aws.amazon.com/blogs/compute/announcing-lambda-microvms-serverless-compute-environments-with-vm-level-isolation-and-near-instant-startup/
AWS::Lambda::MicrovmImage: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-microvmimage.htmlaws-native.lambda.MicrovmImage: https://www.pulumi.com/registry/packages/aws-native/api-docs/lambda/microvmimage/aws-native.lambda.NetworkConnector: https://www.pulumi.com/registry/packages/aws-native/api-docs/lambda/networkconnector/- SST
Taskcomponent and SDK, for the pattern this follows: https://sst.dev/docs/component/aws/task/
Source: anomalyco/sst