Where to Run an Agent on AWS: Lambda, Fargate, Step Functions or AgentCore
“Where should the agent run?” usually gets answered by whatever the team deployed last. The choice deserves more thought, because it fixes how long sessions can last, how state survives failures, and what the agent’s credentials look like. On AWS there are four reasonable answers. This post is about choosing among them.
The model can be the same in all four cases: Bedrock or another provider, called over HTTPS. What differs is the runtime around the loop.
The four options
AWS Lambda. Each invocation runs for up to 15 minutes. You pay per request and duration, and scale to zero. Good for request-response agents that finish in seconds to minutes. For longer work, Lambda durable functions checkpoint each step and let an execution run for up to a year across many invocations, with a replay model you have to design for.
AWS Step Functions. The agent loop is a state machine: model-call steps, tool steps, choice states and waits. Standard workflows run for up to a year with a full execution history, and can pause for human approval with task tokens. Good when the loop’s structure is mostly fixed and you want an audit trail and explicit approval states. Awkward when the loop is highly dynamic.
Amazon ECS on Fargate. A container with no agent-specific session limit, but tasks can still be stopped by deployment, infrastructure events or application failure. Persist recoverable state outside the task. Good for agents that hold long-lived connections (WebSockets, browser automation, persistent MCP connections), keep a lot of in-memory state, or need a particular framework or system dependency. You own scaling, health checks, deployments and session routing.
Amazon Bedrock AgentCore Runtime. A managed runtime built for agents. You deploy your agent code (any framework), and each session runs in its own isolated microVM, currently for up to eight hours. AgentCore also offers separate managed services for identity, memory, gateways to tools and observability. Good when you want session isolation and long sessions without operating the containers yourself. Check the current runtime limits and pricing against a Fargate estimate for your own traffic pattern.
Choosing
| If the agent… | Start with |
|---|---|
| finishes in under a few minutes, request-response | Lambda (function URL with streaming, or behind a queue) |
| runs a fixed multi-step process with approvals and needs an audit trail | Step Functions |
| runs for minutes to hours with a dynamic loop, and you want managed isolation | AgentCore Runtime |
| holds long-lived connections or large in-memory state, or needs custom system dependencies | ECS on Fargate |
| is one open-ended step inside a larger fixed process | Step Functions orchestrating a Lambda or AgentCore step |
Two questions settle most cases:
- What’s the longest a session can run? Minutes: Lambda works. Hours: AgentCore, Fargate or a durable execution model. Days, or waiting on people: Step Functions or durable functions.
- Does the session need a persistent process? If it holds a browser, a socket or a large working set in memory, you need a container or AgentCore’s session microVM. If its state fits in a database row between steps, any option works.
What each one means for the agent’s IAM
The runtime decides which role the agent’s code runs as, and that role is what a prompt-injected agent can use.
- Lambda: the function’s execution role. One role per agent function. Don’t share an execution role across agents with different jobs.
- Step Functions: the state machine role invokes the steps, and each Lambda step has its own role. Keep the state machine role limited to invoking its own steps. It doesn’t need the permissions the steps need.
- Fargate: the task role (what your code uses) is separate from the execution role (what ECS uses to pull images and write logs). The agent’s permissions belong on the task role, and an orchestrator that launches tasks needs
iam:PassRoleon both, scoped with aniam:PassedToServicecondition (why that condition matters). - AgentCore: the runtime’s execution role, plus AgentCore Identity if the agent calls third-party services on a user’s behalf with OAuth. Scope the execution role as tightly as you would a Lambda role. “Managed” doesn’t mean “scoped for you”.
Whatever the runtime, run each distinct agent under its own role, and write the blast-radius note: if this agent is fully compromised, what’s the worst it can do?
Costs that don’t show in the pricing table
- Idle time. Agents spend most of their time waiting for model responses. Lambda bills for that wait. Fargate bills for the container whether it’s working or not. Look at how each option treats I/O wait before comparing prices.
- State plumbing. Lambda and Step Functions push state into DynamoDB or S3 between steps. That’s cheap per step, but it’s code you own and a source of bugs.
- Operations. Fargate means you own deployments, autoscaling policies and session affinity. Managed runtimes charge for doing that for you.
Where this stops
This decision is easy to change early and hard to change late. Keep the agent’s core loop (prompt assembly, tool dispatch, limits) in a library that doesn’t depend on the runtime, and keep the runtime-specific code in a thin adapter. That can reduce migration work, though session routing, identity, durable state and cancellation semantics still need adaptation and tests. Revisit the choice when session length or traffic changes significantly.