#6974·sst

Feat Req: MicroVM sandboxes on Lambda

Author: ekaya97Created Aug 19, 2026Updated Aug 19, 2026

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.Function stops after 15 minutes and keeps no state.
  • sst.aws.Task starts slowly, has no per-instance endpoint, and has no suspend.
  • sst.aws.Service is 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.

python
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() returns properties plus include: [permission({ actions, resources })]. It does not return a running task.
  • The runtime call lives in the SDK, sst/aws/task, as task.run(), task.describe(), and task.stop().
  • Non-Node consumers read the same linked Resource object. Our FastAPI server does this in server/services/pipeline_trigger.py: it reads Resource.PipelineWorker.cluster, .taskDefinition, .subnets, and .securityGroups, then calls ecs:RunTask with boto3.

A MicroVM component fits this split without changing it.

The proposal

1. Component: sst.aws.MicroVm

I suggest MicroVm and not Sandbox.

typescript
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:

typescript
{
  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.

typescript
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 dev has no local analogue. Task solves 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 point Resource at localhost, 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 duration and an idle policy, and default them to short values.

Minimum viable first version:

  1. sst.aws.MicroVm with image, memory, hooks, environment, link, permissions, logging, transform.
  2. Build role, execution role, S3 artifact, MicrovmImage, log group.
  3. getSSTLink() with the properties and permissions above.
  4. microvm.run(), .fetch(), .describe(), .terminate() in sst/aws/microvm.
  5. 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