Limits, Scaling & Runtime vs. Lambda
Quotas, the 2 vCPU/8 GB ceiling, and when to use what
- Explain why AgentCore Runtime is a per-session microVM model rather than stateless FaaS, and how that differs from Lambda
- Describe what triggers a cold start and reuse the `runtimeSessionId` to keep requests on the same warm microVM
- Identify the scaling levers — active session workloads per account and the adjustable `InvokeAgentRuntime` TPS quota — and where to verify their live values
- State the fixed per-session hardware ceiling (2 vCPU / 8 GB) and the payload, streaming, and session-storage limits that bound a workload
- Apply a decision framework to choose AgentCore Runtime vs. Lambda for a given agent workload
AgentCore Runtime is not classic stateless FaaS: every session gets its own dedicated microVM that AWS keeps alive across invocations for up to 8 hours. This lesson sets production expectations — the cold-start/affinity model, the scaling quotas that govern throughput and concurrency, the fixed 2 vCPU / 8 GB per-session hardware ceiling — and closes with a clear decision framework for when to reach for Runtime versus a plain Lambda. Every specific number here is volatile; the lesson teaches the concept and points you at the live quotas page.
- 1Not classic FaaS: a microVM per session, kept warm
- 2Cold starts and microVM affinity
- 3Scaling: the quotas that govern concurrency and throughput
- 4The per-session hardware ceiling and other hard limits
- 5Decision framework: Runtime vs. Lambda
Not classic FaaS: a microVM per session, kept warm
If your mental model of "serverless" is AWS Lambda — a stateless function that spins up, handles one request, and is gone — AgentCore Runtime will surprise you. It is serverless (you hand AWS a container, AWS runs and scales it, you never manage a fleet), but the unit of execution is different.
Every user session gets its own dedicated microVM with isolated CPU, memory, and filesystem, and AWS keeps that microVM alive across invocations for up to 8 hours. You invoke it through the InvokeAgentRuntime data-plane API using an agent runtime ARN plus a caller-supplied runtimeSessionId. Repeated invocations carrying the same session ID land on the same microVM, which still holds the in-memory reasoning context from the previous turn.
That single design choice is the whole reason Runtime exists for agents:
| AWS Lambda | AgentCore Runtime | |
|---|---|---|
| Unit of execution | Stateless invocation | Stateful session (one microVM) |
| Lifetime | Per request (≤15 min) | Up to 8 hours, across many invokes |
| In-memory state between calls | None (don't rely on warm reuse) | Held on the session's microVM |
| Isolation boundary | Function execution environment | Per-session microVM (CPU/mem/FS) |
| Native streaming / long tasks | Limited | First-class (SSE, async, WebSocket) |
Agents are long-lived, stateful, and non-deterministic: they hold a reasoning trajectory, call tools mid-thought, and may run for minutes. Forcing that onto a stateless function means re-hydrating context on every call and fighting the 15-minute wall. Runtime's per-session microVM is purpose-built for it.
One caveat that follows directly from this model: the microVM is ephemeral, durability is your job. In-memory and on-disk state lives only for the microVM's lifecycle — an idle timeout or the 8-hour cap tears it down and sanitizes memory. For state that must survive, configure session storage (a persistent filesystem mount) or use AgentCore Memory for durable conversational state.
Key insight
The session is the billable, schedulable, isolated unit
Almost everything about Runtime keys off the session: isolation (one microVM each), affinity (same ID → same microVM), concurrency quotas ("active session workloads"), and billing (active vCPU-hours and GB-hours while the session works). Internalize "session = microVM" and the rest of this lesson falls out of it.
Cold starts and microVM affinity
A cold start in Runtime means provisioning a fresh microVM. It happens when:
- a request opens a new session (a
runtimeSessionIdAWS hasn't seen, or none supplied — one is generated on first invoke), or - a request is missing affinity to an existing microVM and gets routed to a freshly provisioned one.
The fix is to reuse the same runtimeSessionId for related invocations. That keeps your conversation context and routes you back to the same warm microVM (AWS docs call this microVM "stickiness"). Without a consistent ID, each request may hit a new microVM and pay provisioning latency again.
import json, uuid, boto3
client = boto3.client("bedrock-agentcore") # data plane
# Create ONE session id per user/conversation and reuse it.
# Minimum 33 chars; a UUID (36 chars) satisfies that.
session_id = str(uuid.uuid4())
def ask(prompt: str) -> str:
resp = client.invoke_agent_runtime(
agentRuntimeArn="arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/MyAgent-XXXX",
runtimeSessionId=session_id, # reuse → warm microVM + context
payload=json.dumps({"prompt": prompt}).encode(),
qualifier="DEFAULT",
)
return json.loads("".join(c.decode("utf-8") for c in resp.get("response", [])))
ask("What's the weather in Seattle?") # may cold-start the microVM
ask("And tomorrow?") # same session → warm, keeps contextWhen you call over a raw protocol instead of boto3, the session ID rides in a header — the exact name depends on the protocol:
| Protocol | Session header |
|---|---|
| HTTP / A2A / AG-UI | X-Amzn-Bedrock-AgentCore-Runtime-Session-Id |
| MCP | Mcp-Session-Id |
Always capture the session ID returned in the response and echo it on subsequent calls — your client backend, not AWS, owns the user↔session mapping.
How long is a cold start? AWS publishes no hard number — it is workload-dependent (image size, init code, framework startup) and unverified. Treat cold-start latency as something you measure for your agent, not a constant you can quote. The architectural lever you control is affinity: reuse session IDs, and keep the session warm during long async work by reporting HealthyBusy from /ping so the idle timeout doesn't reap a microVM you still want.
Watch out
A stop-then-reuse is a fresh microVM, not a resume
If a session hits its idle timeout (default 15 min) or the 8-hour max and is torn down, invoking again with the same runtimeSessionId provisions a brand-new microVM with a new ≤8h lifecycle. In-memory and on-disk state from before is gone unless you persisted it (session storage / AgentCore Memory). Reuse buys you affinity while the microVM lives — it is not a magic resume button.
Tip
Don't block the entrypoint
A blocking call inside @app.entrypoint starves the /ping health thread, so AWS thinks the session is unhealthy and can kill it mid-work at the idle timeout. Offload long work to threads or async, and use app.add_async_task(...) / app.complete_async_task(...) so /ping reports HealthyBusy and the session stays alive.
Scaling: the quotas that govern concurrency and throughput
Runtime scales to many concurrent sessions automatically — but "automatically" is bounded by Service Quotas. Two levers matter most for capacity planning, and both are [VOLATILE] — verify the live quotas page before you size anything.
1. Concurrency — "Active session workloads per account." This caps how many sessions can be actively running at once. As researched, it is 1,000 in us-east-1 / us-west-2 and 500 in other regions, and it is adjustable via a quota-increase request.
2. Invoke throughput — InvokeAgentRuntime TPS. Per-agent invocation throughput is throttled at roughly 25 TPS (transactions per second), and this one is adjustable. The streaming and command variants share the same ballpark:
Quota (as researched — [VOLATILE]) | Value | Adjustable? |
|---|---|---|
| Active session workloads / account (us-east-1, us-west-2) | 1,000 | Yes |
| Active session workloads / account (other regions) | 500 | Yes |
InvokeAgentRuntime TPS | ~25 | Yes |
InvokeAgentRuntimeWithWebSocketStream TPS | ~25 | — |
InvokeAgentRuntimeCommand TPS | ~25 | — |
| New sessions / endpoint — container | ~100 TPM | — |
| New sessions / endpoint — direct code | ~25 TPS | — |
| Agents / account · versions / agent | 1,000 each | — |
| Endpoints / agent | 10 | — |
The practical reading: TPS quotas govern how fast you can start invocations; the active-session quota governs how many sessions can be in flight at once. Long agent sessions (the common case) make the concurrency cap the binding constraint far more often than raw TPS. Because every number here drifts between releases and regions, hard-code none of them — read them at runtime or check the console.
# Read the live values for your account/region instead of trusting a doc.
aws service-quotas list-service-quotas \
--service-code bedrock-agentcore \
--region us-west-2
# Request an increase on an adjustable quota (replace the quota code from the list above)
aws service-quotas request-service-quota-increase \
--service-code bedrock-agentcore \
--quota-code L-XXXXXXXX \
--desired-value 50Watch out
Every number on this page is volatile
Quotas, TPS, and regional defaults change between releases and differ by region; some are adjustable and some are not. Treat the values here as "as of writing — verify the live page." The single source of truth is the AgentCore quotas page (linked in Resources). Frame numbers as inputs to verify, never as timeless constants.
The per-session hardware ceiling and other hard limits
Here is the limit that most often forces an architecture decision: the per-session hardware ceiling is 2 vCPU / 8 GB, and it is NOT adjustable. Unlike a Lambda where you dial memory (and proportional CPU) up to multiple GB, or an ECS/EKS task you size freely, a Runtime session is fixed. If a single session genuinely needs more than 2 vCPU / 8 GB of simultaneous compute, Runtime is the wrong host for that step — push the heavy lifting to a Built-in Tool (Code Interpreter), a separate batch/ECS job, or a downstream service, and let the agent orchestrate.
The other hard limits (all [VOLATILE], all on the quotas page) bound payloads and durations:
Limit (as researched — [VOLATILE]) | Value | Adjustable? |
|---|---|---|
| Hardware / session | 2 vCPU / 8 GB | No |
| Max payload | 100 MB | No |
| Streaming chunk size | 10 MB | No |
| Streaming max duration (SSE + WebSocket) | 60 min | No |
| Synchronous request timeout | 15 min | No |
| Async job max duration | 8 hours | No |
| WebSocket frame | 64 KB | No |
| Session storage (persistent FS) / session | 1 GB | No |
| Max Docker image | 2 GB | No |
| Direct-code deploy (compressed / uncompressed) | 250 MB / 750 MB | No |
| Idle session timeout | 15 min (default) | Via API |
| Max session lifetime | 8 hours (default) | Via API |
Two of these are configurable per agent — not via Service Quotas, but through the runtime's LifecycleConfiguration:
# Adjust idle timeout and max lifetime when creating/updating the runtime
# (control plane: bedrock-agentcore-control)
control = boto3.client("bedrock-agentcore-control")
control.update_agent_runtime(
agentRuntimeId="MyAgent-XXXX",
lifecycleConfiguration={
"idleRuntimeSessionTimeout": 1800, # seconds; default 900 (15 min)
"maxLifetime": 28800, # seconds; max 8 hours
},
)Note the contrast: session-lifetime knobs live on the runtime resource (LifecycleConfiguration), while throughput/concurrency knobs are Service Quotas. The hardware ceiling is neither — it is simply fixed.
Session storage deserves a callout: persistent filesystem is capped at 1 GB per session, with roughly 50 MB of FS metadata (~100k–200k files), 200 levels of directory depth, 255-byte filenames, and 4,095-byte symlink targets. It's for working files and checkpoints across a stop/resume — not a data lake.
Example
When 2 vCPU / 8 GB bites
An agent that loads a multi-GB model into memory or runs a CPU-bound simulation inside the entrypoint will OOM or throttle on a single session — the ceiling is hard. The fix is architectural, not a quota request: offload that step (Code Interpreter sandbox, a Batch/ECS job, an inference endpoint) and have the agent call it. Runtime is for the agent's reasoning loop, not for being the compute backend.
Decision framework: Runtime vs. Lambda
Both are serverless; they solve different shapes of problem. Use this as a first-pass filter.
Reach for AgentCore Runtime when the workload is:
- Long-running — minutes to hours (async jobs up to 8h); Lambda caps at 15 min.
- Stateful / session-affine — the agent holds reasoning context across turns and benefits from the same warm microVM.
- Streaming — token-by-token SSE or WebSocket back to a UI (up to 60 min of streaming).
- Large-payload — up to 100 MB in; Lambda's synchronous payload limits are far smaller.
- Agent-framework based — you're running Strands / LangGraph / CrewAI / etc. and want the standardized HTTP/MCP/A2A contract, per-session isolation, and built-in observability without hand-rolling the plumbing.
Reach for Lambda when the workload is:
- Short, stateless, sub-15-minute request/response — a webhook, a quick transform, a tool a Gateway calls. For these, Lambda is typically cheaper and operationally simpler.
| Dimension | AWS Lambda | AgentCore Runtime |
|---|---|---|
| Duration | ≤ 15 min | minutes → hours (async ≤ 8h) |
| State across calls | Stateless | Stateful session (warm microVM) |
| Streaming | Limited | First-class SSE / WebSocket (≤ 60 min) |
| Max request payload | Small (MBs) | Up to 100 MB |
| Isolation model | Execution environment | Per-session microVM |
| Best for | Short stateless functions, glue, simple tools | Long, stateful, streaming agents on a framework |
| Cost shape | Per-request + GB-ms | Active vCPU-hr + GB-hr (I/O wait excluded) |
A useful sanity check on cost: Runtime bills on active vCPU-hours and GB-hours (per-second, I/O wait excluded — so an agent waiting on a model or a tool isn't burning active compute), as of writing roughly $0.0895/vCPU-hr and $0.00945/GB-hr [VOLATILE — verify the pricing page]. For a 200 ms stateless transform invoked millions of times, Lambda's per-request model wins easily. For a 20-minute multi-turn agent that streams and calls tools, Runtime's session model is both the right fit and the sane operational story. These are not mutually exclusive — a common pattern is a Runtime-hosted agent that calls Lambda-backed Gateway targets for the short, stateless tool calls.
Note
It's rarely either/or
The clean architecture is often both: AgentCore Runtime hosts the long-lived, stateful, streaming agent loop, and short stateless work — a price lookup, a CRUD call, a transform — runs in Lambda functions exposed as Gateway targets. Runtime orchestrates; Lambda does the cheap, bounded units.
Try it: Prove affinity, find your real quotas, and make the Runtime-vs-Lambda call
Goal: turn the abstract limits into numbers you've measured for your own account, and practice the decision framework.
Prerequisite: a deployed AgentCore Runtime agent (from the earlier Runtime lessons) and AWS credentials with bedrock-agentcore invoke permission.
- Demonstrate cold start vs. warm. Write a small client that calls
invoke_agent_runtimetwice with a freshruntimeSessionIdeach time, and twice more reusing a single session ID. Time each call. You should observe higher latency when a new microVM is provisioned and lower, more consistent latency on the reused session.pythonimport time, json, uuid, boto3 c = boto3.client("bedrock-agentcore") arn = "arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/MyAgent-XXXX" def call(sid): t = time.time() c.invoke_agent_runtime(agentRuntimeArn=arn, runtimeSessionId=sid, payload=json.dumps({"prompt": "ping"}).encode(), qualifier="DEFAULT") return time.time() - t print("new each time:", [round(call(str(uuid.uuid4())), 3) for _ in range(2)]) sid = str(uuid.uuid4()) print("reused: ", [round(call(sid), 3) for _ in range(2)]) - Read your live quotas. Run
aws service-quotas list-service-quotas --service-code bedrock-agentcore --region <your-region>. Find "active session workloads per account" and theInvokeAgentRuntimeTPS quota. Record the actual values for YOUR region — note they may differ from the numbers in this lesson, and which are marked adjustable. - Tune a lifecycle knob. Using the control-plane client (
bedrock-agentcore-control), callupdate_agent_runtimewith alifecycleConfigurationthat raisesidleRuntimeSessionTimeout(e.g. 1800s). Re-read the runtime withget_agent_runtimeand confirm the change. (Contrast: this is a runtime-resource setting, not a Service Quota.) - Confirm the ceiling is fixed. Inside your entrypoint, log the available CPU/memory (
os.cpu_count(), and read/sys/fs/cgroupmemory limits orpsutil). Confirm it caps at 2 vCPU / 8 GB — and that there is no quota to raise it. - Make the call. For each of these, decide Runtime or Lambda and justify in one sentence: (a) a Slack slash-command that returns a canned status in 300 ms; (b) a research agent that browses for 25 minutes and streams progress to a dashboard; (c) a nightly batch transform of S3 files that finishes in 8 minutes; (d) a LangGraph customer-support agent holding context across a 40-minute conversation. Then describe one architecture where (b) or (d) calls (a) or (c) as a Lambda-backed Gateway target.
Key takeaways
- 1AgentCore Runtime is serverless but NOT stateless FaaS: each session gets a dedicated microVM kept warm across invocations for up to 8 hours — the headline difference from Lambda for agent workloads.
- 2A cold start is a freshly provisioned microVM (new session or missing affinity). Reuse the same `runtimeSessionId` (≥33 chars) to keep context and route back to the same warm microVM; AWS publishes no hard cold-start number.
- 3Scaling is governed by Service Quotas — "active session workloads per account" (concurrency, ~1,000 in us-east-1/us-west-2 and ~500 elsewhere, adjustable) and `InvokeAgentRuntime` TPS (~25, adjustable). All values are volatile; verify the live quotas page.
- 4The per-session hardware ceiling is 2 vCPU / 8 GB and is NOT adjustable; payload (100 MB), streaming-chunk (10 MB), streaming-duration (60 min), sync timeout (15 min), async (8h), and session-storage (1 GB) limits also bound a workload.
- 5Lifecycle knobs (idle timeout, max lifetime) live on the runtime's `LifecycleConfiguration`; throughput/concurrency knobs are Service Quotas; the hardware ceiling is fixed — they are three different control surfaces.
- 6Runtime vs Lambda: long-running / stateful / streaming / large-payload / agent-framework → Runtime; short, stateless, sub-15-minute request/response → Lambda (often cheaper). They compose — Runtime agent calling Lambda-backed Gateway targets is common.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.A teammate says "AgentCore Runtime is just Lambda with a longer timeout." What is the most important correction?
2.Your agent feels slow on every other request because each call seems to re-initialize. What most directly reduces these repeated cold starts?
3.You're sizing a Runtime deployment that will run many simultaneous long-lived agent sessions. Which quota is most likely to be your binding constraint, and what's its nature?
4.Which workload is the clearest fit for AWS Lambda rather than AgentCore Runtime?
Go deeper
Hand-picked sources to keep learning
The single source of truth for every number in this lesson — concurrency, TPS, payloads, durations, the 2 vCPU/8 GB ceiling, session storage. All values are volatile; check here before sizing.
The serverless model — per-session microVM, scaling, and why it isn't stateless FaaS.
Session lifecycle states, microVM stickiness, the ≥33-char session ID, and why your backend owns the user↔session mapping.
Async jobs (≤8h), streaming (≤60 min), /ping + time_of_last_update, and don't-block-the-entrypoint — how to keep a long session warm.
Active vCPU-hour / GB-hour billing (I/O wait excluded) for the Runtime-vs-Lambda cost comparison. All rates are volatile — verify here.
Deep dive on the scaling and isolation model from the AWS ML blog.