AWS Bedrock AgentCore in Production: What It Solves, What It Costs, and What Breaks First
Amazon Bedrock AgentCore, often written as AWS Agent Core, went to preview in July 2025 and reached general availability on October 13, 2025. Search interest has climbed steadily since, and most of what you find is either an AWS launch post or a hello-world tutorial that deploys a single agent and calls it a day. Neither tells you what happens on the Monday after you turn it on for real users.
We have been running multi-agent systems on AgentCore in production since the preview, through a supervisor-and-specialists topology serving a live product, across two regions, with the full set of things that come with that: incident channels, AWS support cases, quota tickets, and a lot of reading of undocumented behavior. This post is the account we wish had existed when we started. It covers what AgentCore is, the single idea that explains most of its behavior, where it genuinely earns its place, and the specific failure modes that will find you first.
What AgentCore Actually Is
AgentCore is not an agent framework and it is not a model. It is a set of managed services for running agents that you wrote yourself, in whatever framework you already use. LangGraph, Strands, CrewAI, or a hand-rolled loop all deploy the same way. You bring a container image (or, more recently, a zipped code package) that serves HTTP on an invocations endpoint and a ping endpoint, and AgentCore runs it.
The platform splits into modular pieces you adopt independently. Runtime executes your agent. Memory provides short-term and long-term persistence built for conversational state. Gateway turns APIs and Lambda functions into MCP tools with auth attached. Identity handles inbound authorization and outbound credentials for the services your agent calls on a user's behalf. Code Interpreter and Browser give agents sandboxed execution and a real browser. Observability emits OpenTelemetry and feeds CloudWatch. Since GA, AWS has added Policy for Cedar-based guardrails on tool calls, Evaluations for scoring agent behavior, and an agent registry.
Two properties matter more than the feature list. First, it is model-agnostic: nothing forces you onto Bedrock-hosted models, and routing every call through your own LLM gateway to OpenAI, Anthropic, or anything else works fine. Second, it speaks MCP, A2A, and AG-UI as first-class protocols, so agent-to-agent topologies do not require you to invent a transport.
Pricing follows consumption. Runtime bills roughly $0.0895 per vCPU-hour and $0.00945 per GB-hour, measured on active CPU and peak memory, and it does not charge CPU during I/O wait. That last detail is the one that makes AgentCore economically sane for agents, which spend most of their wall clock waiting on model tokens rather than computing.
The One Idea That Explains Everything: The Session Is the Unit
Almost every surprise in AgentCore traces back to a single design decision. The unit of compute is not the request. It is the session.
When you invoke a runtime with a session ID, AgentCore provisions a dedicated microVM for that session and routes every subsequent invocation carrying the same ID back to the same microVM. The environment stays warm between calls. Your in-process state survives. When the session ends, the microVM is torn down and its memory is sanitized, so there is a hard isolation boundary between one user's agent and another's.
The session ID travels in a header, and which header depends on the protocol your runtime is configured for:
| Protocol | Session header |
|---|---|
| HTTP | X-Amzn-Bedrock-AgentCore-Runtime-Session-Id |
| A2A | X-Amzn-Bedrock-AgentCore-Runtime-Session-Id |
| AG-UI | X-Amzn-Bedrock-AgentCore-Runtime-Session-Id |
| MCP | Mcp-Session-Id |
Session IDs must be at least 33 characters. This is documented, it is easy to miss, and if you are generating short IDs from your own conversation table you will hit it. A UUID clears the bar; an incrementing integer does not.
A session moves through three states. Active means it is processing a request, running a command, or doing background work that your agent declares by answering HealthyBusy on the ping endpoint. Idle means provisioned but doing nothing. Stopped means the microVM is gone, whether from the idle timeout (default 15 minutes), the maximum lifetime (default 8 hours), an explicit stop call, or a failed health check. A stopped session is not a dead session: the next invocation provisions fresh compute under the same ID, with the whole lifecycle clock reset.
Understand that and most of what follows becomes predictable.
What AgentCore Genuinely Does Well
The isolation model is real engineering value, not a marketing line. Agents perform privileged operations with borrowed credentials, and their behavior is non-deterministic by construction. Giving each session its own microVM turns a probabilistic process into a deterministic security boundary. Because each agent process is effectively single-tenant for the life of the session, you can hold a user's JWT in process memory without inventing a scheme to keep tenants apart. That eliminates an entire category of design work, and an entire category of incident.
Keeping your framework is the second real benefit. AgentCore does not ask you to rewrite your graph into a proprietary DSL. The agent that runs on your laptop is the agent that runs in the runtime, which keeps local development honest and keeps you free to leave.
The third is the work you do not do. Long-running, stateful, resumable execution per user is a genuinely hard thing to build on Kubernetes or on Lambda. If you have ever tried to keep a multi-step agent alive across a fifteen-minute tool chain while a Go service holds a reliable background job for it, you know the shape of that engineering. AgentCore hands you sessions of up to 8 hours on microVMs, request timeouts of 15 minutes, streaming connections up to 60 minutes, and asynchronous jobs up to 8 hours. For teams that need to ship an agent this quarter rather than build a durable execution platform, that trade is usually correct.
Weakness One: Capacity Is Measured in Sessions, Not Requests
This is the failure that will find you first, and it is arithmetic, not a bug.
Your intuition from web services says concurrency equals in-flight requests. Under AgentCore, a session holds a microVM from its first invocation until it stops, which by default means fifteen minutes after the user's last message, and potentially for eight hours. The user finished the conversation ten minutes ago. The microVM is still counted.
Now multiply by topology. A supervisor-and-specialists design does not open one session per conversation; it opens one per participating agent. We measured a system where a single new conversation per minute held roughly 65 concurrent sessions at steady state, and closer to 90 once organic traffic was included. Not 1. Not 3. Sixty-five, because the supervisor was reclaimed after five idle minutes while the specialists ran to their configured maximum lifetime.
At GA, the default quota was 1,000 concurrent sessions in the large US regions and 500 elsewhere. That arithmetic put a barely-launched product at ten percent of its ceiling, and made a public rollout at ten conversations per minute mathematically impossible without a quota increase. In July 2026, AWS raised the defaults to 5,000 active concurrent sessions in US East (N. Virginia) and US West (Oregon) and 2,500 in other regions, along with 200 agent interactions per second and 25 new sessions per second. The size of that increase tells you how many teams walked into the same wall.
What to do about it. Do the multiplication before launch, not after: agents per conversation, times conversation arrival rate, times session lifetime, is your steady-state session count. Tune idleRuntimeSessionTimeout and maxLifetime in LifecycleConfiguration deliberately rather than accepting eight hours; on a chat product, five minutes idle and thirty minutes maximum is often closer to right, and cutting the maximum lifetime matters more than cutting the idle timeout when specialist agents sit idle inside a long session. Implement the ping endpoint properly, including HealthyBusy, so the platform can distinguish an agent thinking from an agent abandoned. And file for the quota increase early, because the ticket takes longer than the code.
Weakness Two: Concurrency Within a Session Is Your Problem
One microVM per session is a strong guarantee for isolation and a sharp edge for concurrency. Two invocations landing on the same session ID at the same time are not two requests to a load-balanced pool. They are two requests to one machine that may not exist yet.
The documented behavior is a retryable HTTP 409 RetryableConflictException while the service is provisioning or tearing down a session, which you handle with short exponential backoff. The behavior teams actually report is less tidy: overlapping invocations on a warm-but-busy session can surface as a status blob inline in the streaming response rather than as an exception, along the lines of a processing marker followed by a null response flagged as a duplicate. Nothing throws. Your error rate looks fine. The blob flows straight through to the user unless you filter it.
The mitigations are all on your side of the line. Serialize per session before the request reaches AWS, using a distributed lock or a queue keyed on session ID, because a mutex inside the Python process cannot help when the process has not been provisioned yet. Enforce turn-taking in the UI so a user cannot send a second message while the first is in flight. Never fan out concurrent calls onto a single session ID, including well-intentioned warm-up calls fired in parallel with the real invocation. And filter the response stream for platform-level control messages instead of trusting that everything you receive is agent output.
Weakness Three: Cold Starts, and the Client Timeouts Around Them
Provisioning a microVM and pulling your image takes time. Container images are capped at 2 GB, and a typical Python agent with a full framework dependency tree lands somewhere between 800 MB and 1 GB uncompressed, which is enough to make cold starts noticeable. The newer direct code deployment path (250 MB compressed, 750 MB uncompressed) exists largely to attack this, and it is worth using when your agent fits.
The subtle part is not the cold start. It is the mismatch between platform timeouts and client timeouts. AgentCore's invocation ceiling is generous, but the HTTP client in your agent or your backend probably defaults to something like thirty seconds. During elevated cold starts, the platform completes the request at forty seconds and your client gave up at thirty. The result is an error that exists only on your side: nothing appears in CloudWatch, because from AWS's perspective the request succeeded.
Two things compound it. Client libraries wrap transport errors in ways that lie to you. An A2A client that raises a 503 on a connection timeout will send you hunting for a server-side outage that never happened, and you can burn a day before noticing the 503 is synthetic. And discovery calls are the worst place for this, because fetching an agent card at startup is exactly the moment every session is cold.
The fixes are unglamorous and effective. Set client timeouts to match the platform ceiling rather than a habit inherited from REST services. Cache static metadata such as agent cards with a short TTL, since they change on deploy, not per request; this alone removes cold-start sensitivity from the discovery path entirely. Retry with a backoff that spans a cold start rather than one that gives up inside it. Shrink the image. And be careful with initialization flags: code that sets an initialized sentinel unconditionally, even when a dependency failed to resolve, will poison a session into degraded mode for the entire lifetime of that microVM. One bad cold start becomes thirty minutes of quiet wrongness.
Weakness Four: Headers Will Cost You an Afternoon
Three separate header traps sit on top of each other, and they interact.
The first is the session header itself, which changes name by protocol. A team that moves an agent from HTTP to MCP and keeps sending X-Amzn-Bedrock-AgentCore-Runtime-Session-Id gets no error, just a new microVM per request and an unexplained latency regression from constant cold starts.
The second is the allowlist. Passing your own context through headers is a good pattern, and it is the right way to hand an agent a JWT: a token in a header stays out of the request body, which means it stays out of your framework's checkpoints and out of your traces, where it would otherwise be persisted in cleartext across every span of the conversation. But headers are not forwarded by default. You must declare a requestHeaderAllowlist on the runtime, up to 20 headers, each value capped at 4 KB. There is a long restricted list you cannot forward at all, everything prefixed x-amz- and x-amzn- is reserved except the X-Amzn-Bedrock-AgentCore-Runtime-Custom- prefix, and the Authorization header only comes through when the runtime is configured with a custom JWT authorizer. Worse for operations: UpdateAgentRuntime is a full PUT, so an update that omits the header configuration silently drops it. Put the allowlist in your infrastructure-as-code, or it will be removed by an unrelated deploy.
The third has nothing to do with AgentCore and everything to do with what sits between you and it. Load balancers and reverse proxies drop HTTP headers containing underscores by default. A header named space_id disappears somewhere in the path while space-id arrives intact, and the symptom is not an error, it is data quietly going to the wrong destination. We lost real time to exactly this on the observability path, where telemetry routing depended on a header that an ALB was silently discarding. If a header matters, spell it with hyphens, and verify it end to end by capturing the raw request at the far side rather than trusting that what you sent is what arrived.
Weakness Five: Traces Look Right Until You Look Closely
AgentCore emits OpenTelemetry, integrates with CloudWatch GenAI observability, and exports to third-party backends such as Arize, Langfuse, or Datadog. The plumbing works.
The failure mode is orphaned spans. Context propagation across the runtime boundary, and then through a collector, can lose parent span IDs, and what you get is not an error but a flat pile of spans where you expected one tree per conversation. Every span is present. The structure is gone. Since the whole point of agent observability is seeing which tool call inside which reasoning step produced the wrong answer, a flat span list is close to useless.
Diagnosing this by reading collector configuration is slow. Bisecting the pipeline is fast. Export directly from the runtime to your observability backend, bypassing the collector, and see whether the hierarchy is correct. If it is, the runtime and your instrumentation are fine and the problem is in the pipeline. We resolved a multi-week investigation in an afternoon this way, and the answer turned out to be a collector-side autocollector clearing the context that carried span parentage. Also send raw telemetry to a local endpoint through a tunnel and read the actual headers and payload; it is the only way to prove what left the runtime rather than what you believe left it.
Weakness Six: Durability Is Handed to You, Semantics Are Not
AgentCore gives you an execution environment that survives across invocations. It does not give you opinions about what your agent should persist, and that is where the sharp edges live.
If your framework checkpoints after every graph node, a mid-turn failure leaves partial state on disk. Configuring the checkpointer to write only on successful exit is a one-line change that covers most of it, and it is worth doing early. It does not cover everything: an agent that fails but stays alive long enough to persist its own error will happily checkpoint that error as conversation history, and you need explicit handling for that case.
Decide who owns conversation history before you build. If both the runtime and your backend store it independently, they will diverge, because the agent can save its response and then fail to deliver it. Making the backend the single source of truth is the cleaner architecture; keeping both in sync is faster to ship. Either is defensible. Not deciding is not.
Finally, if you invoke the runtime fire-and-forget so your API can return immediately, understand that you have given up the failure signal. The caller has no idea whether the agent died, and it will wait forever for a callback that is not coming. You need a timeout, a heartbeat, or an explicit terminal state written by the agent in a finally block. Pick one deliberately.
Weakness Seven: The Development Environment Economics
Production is not where most of the friction lives. Preview environments are.
AgentCore Memory defaults to 150 resources per account per region. One memory resource per agent per environment, multiplied by five agents and a team spinning up ephemeral environments per pull request, exhausts that in weeks, and the failure surfaces as a Terraform apply dying on ServiceQuotaExceededException during CreateMemory. Ephemeral runtimes documented to expire after 24 hours do not always expire, so orphaned resources accumulate against your quota with no one watching. Provisioning has its own rough edges: a first apply can fail on a CloudWatch log group that already exists and needs importing before the second apply succeeds.
Budget for this. Decide early whether every engineer gets a full set of agent runtimes or whether a shared pool is enough, write a reaper for orphaned resources rather than trusting the documented TTL, and treat agent provisioning as real infrastructure with real quota planning, not as a developer convenience.
The Relationship With AWS
Two things are worth saying here, because both affect how you should plan.
The first is how AgentCore relates to the rest of AWS. It is deeply integrated and not especially locking. Auth is IAM, images live in ECR, logs and metrics land in CloudWatch, capacity is governed through Service Quotas, and provisioning is Terraform like everything else. You are not forced onto Bedrock-hosted models, which means AgentCore composes cleanly with an LLM gateway in front of OpenAI or Anthropic. The coupling you actually take on is operational rather than architectural: your agent code stays portable, but your capacity planning, your quota tickets, and your incident response become AWS-shaped. Region matters more than usual, since limits differ between US regions and everywhere else, and a multi-region deployment inherits two different ceilings.
The second is how AWS behaves as a partner on a service this young. Our experience has been good, with a specific caveat. The AgentCore service team is reachable through support and gives concrete answers rather than boilerplate: on one investigation they confirmed a platform-side infrastructure issue in a US region that had elevated cold-start latency for multiple customers over a three-day window, explained why it produced no errors in our CloudWatch metrics (the requests succeeded on their side after our client had already timed out), and returned specific mitigations including the agent-card caching advice above. That is a useful relationship to have.
The caveat is what it implies. A managed service less than a year past GA will have platform-side events that your dashboards cannot see, and behavior that is real but undocumented until someone asks. AWS has been shipping against exactly these complaints, with the 5x quota increase, direct code deployment for faster starts, and the header allowlist all landing since GA. That responsiveness is the strongest argument for betting on AgentCore. It is also the reason to build your own timeouts, retries, and independent monitoring rather than assuming the platform's error metrics are the whole truth.
If you are evaluating this, engage AWS early rather than after the incident. Get your account team involved, file quota increases before you need them, and open a support case the moment you see behavior you cannot explain. Vindler works through the AWS Partner Network, and the quickest wins we have had on this platform came from a direct line to the service team, not from more reading.
When AgentCore Is the Right Call, and When It Is Not
Use it when sessions are long and stateful, when isolation between users is a requirement you would otherwise have to engineer, when your agents call privileged tools on a user's behalf, when you want to keep your existing framework, and when you are already on AWS and the operational coupling costs you nothing you were not already paying.
Look elsewhere when your agent is a short, stateless request-response that finishes in seconds, because Lambda or a container behind a load balancer will be cheaper, faster to start, and simpler to reason about. Look elsewhere when you have a hard latency floor that cold starts violate and no way to keep sessions warm. Look elsewhere when you need control over the execution environment beyond what the runtime exposes. And think carefully if you are not otherwise on AWS, since the value here is largely in the integration you would be adopting from scratch.
The honest summary is that AgentCore removes a real and expensive category of infrastructure work, and hands you back a smaller category of unfamiliar failure modes. That is usually a good trade. It is only a good trade if you know what the new failure modes are before they reach your users.
Pre-Launch Checklist
- Compute steady-state concurrent sessions as agents per conversation, times conversation arrival rate, times session lifetime. Compare against your regional quota, in every region you deploy to.
- Set
idleRuntimeSessionTimeoutandmaxLifetimeexplicitly. Do not ship on the eight-hour default. - Implement the ping endpoint with
HealthyBusyso the platform can tell a working agent from an abandoned one. - Generate session IDs of at least 33 characters, and confirm you are sending the header your protocol expects.
- Serialize invocations per session ID outside the runtime, and enforce turn-taking in the UI.
- Filter platform control messages out of the response stream before rendering anything to a user.
- Align client HTTP timeouts with platform ceilings, retry with a backoff that spans a cold start, and cache agent cards with a short TTL.
- Declare
requestHeaderAllowlistin infrastructure-as-code, use hyphens rather than underscores in header names, and verify propagation by capturing the raw request at the far end. - Validate your trace hierarchy end to end before launch, not after the first incident, by exporting directly to your backend as a control.
- Decide who owns conversation history, configure checkpoint durability on exit, and give fire-and-forget invocations an explicit failure signal.
- Plan development-environment quotas, especially AgentCore Memory at 150 resources per region, and write a reaper for orphaned ephemeral resources.
- File quota increases and open your AWS support relationship before the rollout, not during it.
Working With Us
We build and operate multi-agent systems on AWS, including production deployments on Bedrock AgentCore across multiple regions since its preview. If you are evaluating AgentCore, migrating agents onto it, or debugging one of the failure modes above, we can help. Book a call or read more about how we approach multi-agent architecture and agent evaluation at scale.
Vindler Solutions, August 19, 2026. Version 1.0.




