DEV Community

Rahul Pandya
Rahul Pandya

Posted on

AWS Lambda 90-Minute Timeout: Setup, Cost and Catches (2026)

If you've built anything real on Lambda, you've seen this line in your logs at least once:

Task timed out after 900.00 seconds

And then you did what everyone does. You split the job into chunks, or chained a few functions together with Step Functions, or saved progress to DynamoDB so the next run could pick up where the last one died. Or you gave up and moved the whole thing to Fargate.

The limit went from 5 minutes in 2014 to 15 minutes in 2018. Then it stayed there for eight years.

On September 9, 2026, AWS finally raised it. Lambda functions can now run for up to 90 minutes. That's 5,400 seconds.

But there's a catch that most headlines skip, and it changes everything: this only works on Lambda Managed Instances. That's a different way of running Lambda, with a very different bill. If you just bump the timeout on your normal function, it won't work.

So let's go through what actually changed, how to set it up, what it really costs, and when it's cheaper than Fargate (and when it's not even close).

What changed, and what didn't

The 90-minute limit doesn't apply to every kind of invocation. This is the part that confuses people, so here it is in one table:

How the function is called Max timeout Examples
Asynchronous 90 min S3 events, SNS, EventBridge, InvocationType=Event
Event source mapping 90 min SQS, Kinesis, DynamoDB Streams, MSK, self-managed Kafka
Event source mapping from Amazon MQ or DocumentDB 15 min Still capped
Synchronous 15 min API Gateway, ALB, normal SDK calls
Anything on standard Lambda 15 min Every function not on Managed Instances

A few other things worth knowing:

  • Synchronous calls get cut off at 15 minutes even if your timeout says 5,400. The config still shows 5,400, so nothing warns you.
  • The Init phase (your startup code) is still limited to 15 minutes.
  • You don't need to change your code. Same runtime, same handler, same IAM role, same VPC.
  • AWS doesn't charge extra for the longer timeout itself. You pay normal Managed Instances pricing, which we'll get to.
  • It works with durable functions too. Each async invocation inside a durable function on Managed Instances can run 90 minutes, and the full durable execution can last up to a year.

Wait, what are Lambda Managed Instances?

Normal Lambda runs each request inside its own tiny Firecracker microVM. Lambda Managed Instances, which AWS launched in late 2025, work differently. Your function runs on EC2 instances inside your own VPC, and AWS still takes care of those instances for you: launching them, patching them, scaling them.

There are three pieces:

  1. A capacity provider. This is where you say which VPC and subnets to use, which CPU architecture, which instance types are allowed, and the maximum number of vCPUs. It's also the security boundary.
  2. EC2 instances. Lambda launches these in your account. By default it runs three of them across Availability Zones.
  3. Execution environments. These are containers on the instances, and each one can handle many requests at the same time (up to 1,600).
flowchart LR
    E[SQS / S3 / EventBridge] --> F[Lambda function version]
    F --> CP[Capacity provider]
    CP --> I1[EC2 instance AZ-a]
    CP --> I2[EC2 instance AZ-b]
    I1 --> X1[Execution environments]
    I2 --> X2[Execution environments]

This setup changes a few things you might be used to.

There are no cold starts, because requests go to environments that are already running. The flip side is that there's no scale-to-zero either. The instances keep running, and keep costing money, even when nothing is happening.

Your code also has to handle several requests at once in the same environment. If you write to a fixed file like /tmp/output.csv, or keep request data in global variables, you'll get strange bugs.

And AWS is clear that containers are not a strong isolation boundary between functions. If you have workloads that shouldn't trust each other, give them separate capacity providers.

You can also pick the memory-to-vCPU ratio (2, 4 or 8 GiB per vCPU) and specific instance families if you need them, including the new Graviton5 C9g and M9g instances.

Setting it up

Here's the CLI version. The console follows the same steps under Lambda > Capacity providers.

1. Create two IAM roles

You need an execution role (what the function itself can access) and an operator role (which lets Lambda manage the EC2 instances for you). Both trust lambda.amazonaws.com.

cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "lambda.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

# Execution role
aws iam create-role --role-name LongJobExecutionRole \
  --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --role-name LongJobExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

# Operator role
aws iam create-role --role-name LongJobOperatorRole \
  --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --role-name LongJobOperatorRole \
  --policy-arn arn:aws:iam::aws:policy/AWSLambdaManagedEC2ResourceOperator
Enter fullscreen mode Exit fullscreen mode

If this is the first capacity provider in your account, you'll also need iam:CreateServiceLinkedRole.

2. Create the capacity provider

Use subnets in at least two Availability Zones:

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

aws lambda create-capacity-provider \
  --capacity-provider-name long-jobs \
  --vpc-config SubnetIds=[subnet-aaa,subnet-bbb],SecurityGroupIds=[sg-123] \
  --permissions-config CapacityProviderOperatorRoleArn=arn:aws:iam::${ACCOUNT_ID}:role/LongJobOperatorRole \
  --instance-requirements Architectures=[arm64] \
  --capacity-provider-scaling-config MaxVCpuCount=30
Enter fullscreen mode Exit fullscreen mode

Think of MaxVCpuCount as your spending ceiling. Start small.

3. Create the function

REGION=$(aws configure get region)

aws lambda create-function \
  --function-name long-job \
  --runtime python3.13 \
  --handler app.handler \
  --zip-file fileb://function.zip \
  --role arn:aws:iam::${ACCOUNT_ID}:role/LongJobExecutionRole \
  --architectures arm64 \
  --memory-size 4096 \
  --timeout 5400 \
  --capacity-provider-config LambdaManagedInstancesCapacityProviderConfig={CapacityProviderArn=arn:aws:lambda:${REGION}:${ACCOUNT_ID}:capacity-provider:long-jobs}
Enter fullscreen mode Exit fullscreen mode

4. Publish a version

This one catches people. On Managed Instances, only published versions run. Nothing happens until you publish:

aws lambda publish-version --function-name long-job
Enter fullscreen mode Exit fullscreen mode

Give it a few minutes. Lambda is launching real EC2 instances in the background.

5. Invoke it asynchronously

Synchronous calls still stop at 15 minutes, so test with an async call:

aws lambda invoke \
  --function-name long-job:1 \
  --invocation-type Event \
  --cli-binary-format raw-in-base64-out \
  --payload '{"minutes": 30}' \
  response.json
Enter fullscreen mode Exit fullscreen mode

A simple handler to prove it runs past 15 minutes:

import time

def handler(event, context):
    minutes = event.get("minutes", 20)
    for i in range(minutes):
        time.sleep(60)
        print(f"Still running: minute {i + 1}")
    return {"done": True, "minutes": minutes}
Enter fullscreen mode Exit fullscreen mode

If you use AWS SAM, the timeout is just one line:

LongJobFunction:
  Type: AWS::Serverless::Function
  Properties:
    Runtime: python3.13
    Handler: app.handler
    Timeout: 5400
    MemorySize: 4096
Enter fullscreen mode Exit fullscreen mode

You attach the capacity provider in the same template. Check the SAM docs for the current property names.

What it really costs

AWS says the longer timeout is free, and that's true. But the real question is what the capacity behind it costs.

Managed Instances pricing has three parts:

  1. Requests: $0.20 per million, same as normal Lambda.
  2. EC2 instances: regular EC2 pricing for the instances in your capacity provider. Savings Plans and Reserved Instances apply here.
  3. A management fee: 15% of the EC2 on-demand price. Savings Plans don't reduce this part.

What's missing is the per-millisecond duration charge. You pay for instances being up, not for your code running. That's why a 90-minute run doesn't cost more than a 1-minute run on the same instance. It's also why idle time isn't free.

The worst case: a job that runs now and then

Let's put numbers on it with a pretty normal job, the kind that hits the 15-minute wall today:

  • Runs twice a day, about 45 minutes each time
  • Needs 2 vCPU and 4 GB of memory
  • us-east-1, Graviton (arm64), on-demand prices
Option How it's billed Monthly cost (approx.)
Standard Lambda Not possible, stops at 15 minutes n/a
Managed Instances (3 x m7g.large, always on) 3 x $0.0816/hr x 730 hrs = $178.70, plus 15% fee = $26.81 ~$205
ECS Fargate (arm64, 2 vCPU / 4 GB, only while running) ~$0.079/hr x 45 hrs of runtime ~$3.60

No, that's not a typo. For a job that only runs now and then, Managed Instances can cost around 50 times more than Fargate. Lambda keeps three instances running around the clock whether your job is running or not.

But that example is the worst case for anything that's always on. So let's look at the other side.

When Managed Instances are cheaper than Fargate

One thing first: on plain on-demand pricing, the two cost about the same for the same machine size. A 2 vCPU / 8 GB Fargate task (arm64) costs about $0.093 per hour. An m7g.large (also 2 vCPU / 8 GB) on Managed Instances costs $0.0816 plus the 15% fee, so about $0.094 per hour. Basically a tie.

Managed Instances only win when something tips the balance. There are two big ones.

Scenario 1: Steady 24/7 work with a Savings Plan

Say you have a queue that never really empties, and it keeps 12 vCPU and 48 GB of memory busy all day, every day.

Fargate (6 tasks, 2 vCPU / 8 GB) Managed Instances (6 x m7g.large)
On-demand ~$408/month ~$411/month ($357 EC2 + $54 fee)
With a 3-year Savings Plan ~$196/month (up to 52% off) ~$154/month ($100 EC2 at 72% off + $54 fee)

With commitments in place, Managed Instances save about $42 a month, roughly 21%, or around $500 a year.

Notice the fee, though. The 15% is always charged on the on-demand price, so even with a big discount you still pay that $54. That's why the saving here is real but modest.

Scenario 2: Lots of long jobs that mostly wait

This is where Managed Instances really shine, and it's getting very common with AI workloads.

Imagine jobs that run 20 to 60 minutes but spend most of that time waiting: calling an LLM, polling an external API, scraping slow websites. Each job needs about 0.5 GB of memory and barely any CPU. At peak you have 200 of them running at once, all day.

On Fargate, the usual setup is one job per task. The smallest task that fits is 0.25 vCPU / 1 GB, and you're paying for 200 of them.

On Managed Instances, one execution environment can handle many requests at the same time. So those 200 jobs share a handful of instances. 200 jobs x 0.5 GB is 100 GB, which fits on 8 x r7g.large (16 GB each) with some room to spare.

Fargate (200 tasks, 0.25 vCPU / 1 GB) Managed Instances (8 x r7g.large)
Compute ~$1,702/month ~$625/month EC2
Management fee n/a ~$94/month
Requests n/a under $1/month
Total (on-demand) ~$1,702/month ~$719/month

That's about $983 saved every month, around 58%, or roughly $11,800 a year. And that's before any Savings Plan.

To be fair to Fargate: you could get similar packing by writing your own worker that runs many jobs at once inside one task. But then you're building and running your own queue polling, concurrency and retry logic. With Managed Instances, you get that from the normal Lambda model.

All prices are approximate us-east-1 rates at the time of writing, arm64/Graviton. Savings Plan discounts are the best-case "up to" figures for 3-year plans, so real discounts are often lower. Check your own numbers in the AWS Pricing Calculator.

The simple rule

  • Jobs that run now and then: Fargate wins by a lot
  • Steady 24/7 work, on-demand: about a tie
  • Steady 24/7 work with a Savings Plan: Managed Instances, by about 20%
  • Many long jobs that mostly wait on network calls: Managed Instances, by 50% or more

One more option worth knowing: if your jobs can handle being interrupted, Fargate Spot can be up to 70% cheaper than regular Fargate, which may beat both.

So when should you use it?

Your situation What I'd use
Long jobs on a busy, steady queue Lambda Managed Instances
Many long jobs waiting on LLMs or APIs Lambda Managed Instances
A long job that runs a few times a day ECS Fargate or AWS Batch
A job you can split into steps Durable functions on standard Lambda
A long job where restarting from zero is expensive Managed Instances plus durable functions
A workflow with branches, retries and approvals Step Functions
An HTTP request that needs more than 15 minutes Return a job ID and process it async

My honest view: if you already run steady workloads on Managed Instances, this is a free win, and you can probably delete a fair bit of Step Functions glue. If you don't, don't adopt Managed Instances just for one long job. Fargate will be much cheaper.

Things that will bite you in production

These come from the AWS docs, plus what naturally goes wrong when a function runs six times longer than before.

Your SQS visibility timeout needs to be at least 6x the function timeout. For 90 minutes, that's 9 hours. Lambda checks this when you create the trigger, but it won't stop you from changing things later. Get it wrong and the same message gets processed twice.

NAT Gateway drops idle connections after 350 seconds. Open a database connection, work for 20 minutes, then try to use it, and it may be gone. Send keep-alives or reconnect before using it.

Tokens can expire halfway through. If your function grabs a temporary token at the start, make sure it lasts the full run, or refresh it.

Longer runs mean more chances of duplicates. Lambda doesn't promise exactly-once processing, and async calls retry twice by default. A job that fails at minute 80 might start over from the beginning. Make it idempotent. Powertools for AWS Lambda has a utility for this.

New concurrency bugs. Several requests share one environment, so use unique file names in /tmp (the request ID works well) and keep request data out of global variables.

Deploys are slow. Publishing a version launches EC2 instances, so it takes minutes, not seconds. Plan your pipeline and rollbacks around that.

And the classic one: if the job is triggered through API Gateway, it still stops at 15 minutes, with no warning.

Common errors

Error Likely cause Fix
Timeout above 900 rejected Function is on standard Lambda Attach it to a capacity provider
Stops at exactly 15 minutes Called synchronously Call it async or through a queue
Stops at 15 minutes from Amazon MQ or DocumentDB Those sources are still capped Put SQS in front
Same message processed twice SQS visibility timeout too short Set it to at least 6x the function timeout
Invoke fails right after creating the function No published version, or instances still starting Publish a version and wait a few minutes
Capacity provider creation fails Missing iam:CreateServiceLinkedRole Add it for first-time setup

Before you ship it

  • [ ] Function is on a capacity provider, and a version is published
  • [ ] It's triggered async or through SQS, Kinesis, DynamoDB Streams, MSK or Kafka
  • [ ] SQS visibility timeout is at least 6x the function timeout
  • [ ] Code handles concurrent requests (unique /tmp paths, no shared request state)
  • [ ] The job is idempotent, or checkpoints with durable functions
  • [ ] Connections survive long idle gaps
  • [ ] Tokens last the whole run, or get refreshed
  • [ ] MaxVCpuCount is set as a cost ceiling
  • [ ] You've compared the cost with Fargate for your actual usage pattern

Wrapping up

People have been asking for longer Lambda timeouts for years, and this finally delivers. A lot of workaround code can go.

But it isn't "the same Lambda, just six times longer." It's a feature of a different compute model, with always-on instances and a different kind of bill. For an occasional job, it can cost 50 times more than Fargate. For a busy queue full of long jobs that mostly wait, it can save you more than half.

So do the math for your own workload before you switch. If you're already on Managed Instances, turn it on today. If you've got one long job that runs now and then, leave it on Fargate or Batch and save yourself a surprise at the end of the month.

Have you tried the 90-minute timeout yet? Did the cost surprise you? Let me know in the comments.

If you care about AWS costs, you might also like my AWS FinOps agent build and my ECS vs EKS comparison.

Further reading

Top comments (0)