Category Archives: Lambda

Lambda's 90-Minute Timeout — Lambda Is Slowly Becoming EC2

I have a favorite AWS service, and it’s not a close race. It’s Lambda.

I’ve said this out loud in enough architecture reviews that people roll their eyes at me. But I mean it, and the reason is embarrassingly simple: Lambda is where my ideas go to become real. When I have a half formed thought at 11pm — “what if I wired this webhook to that API and dropped the result in DynamoDB?” — Lambda is the surface where that thought turns into running code before I lose the plot. No instance to launch. No AMI to pick. No security group to reason about. No patching schedule looming in the back of my head. I write the handler, I deploy, it runs. If it’s a bad idea, I delete it and pay nothing for the privilege of having been wrong.

That frictionlessness is worth more than it sounds, and I say that as someone with scar tissue. I’ve been running EC2 since 2009, back in the pre-VPC days when “the cloud” meant EC2-Classic, elastic IPs you had to babysit, and a security model that felt like leaving your front door propped open with a brick. Standing up a prototype in 2009 meant provisioning an instance, SSHing in, installing your runtime, configuring a service, and then — the part everyone forgets — owning that box forever. Patching it. Watching its disk fill up. Wondering if it was still running three months later, quietly costing you money. Lambda erased all of that. For POCs and prototypes, it is the single best tool I have ever used, because it lets me test options fast and throw the losers away without ceremony.

So this post is a little bittersweet. Because the thing I love about Lambda — that it hides the infrastructure — is exactly the thing that’s slowly eroding.

The news: 90 minutes on Managed Instances

On September 9, 2026, AWS announced that Lambda Managed Instances now support a 90-minute function timeout — six times the classic 15-minute ceiling that has defined Lambda’s mental model for years.

A couple of important qualifiers, because the headline oversimplifies. Lambda Managed Instances are a newer execution mode where AWS provisions and manages longer lived compute behind your function, letting you choose capacity providers — think C9G (compute optimized) versus M9G (general purpose) — rather than only tuning a memory slider. The 90-minute timeout applies to asynchronous invocations and event-source-mapping (ESM) flows — queues, streams, event driven fan in. It does not apply to synchronous request/response invocations, which is the right call: no sane API gateway should hold a connection open for an hour and a half. Pair this with durable functions and the existing 1-year ceiling on async event retention, and a picture emerges. Lambda is quietly absorbing workloads that used to be EC2’s birthright, one feature at a time. The AWS Compute Blog deep dive lays out the mechanics if you want the full spec.

When 90 minutes actually matters

To be fair — and I want to be fair, because I love this service — there are real workloads that hit the 15-minute wall hard and hurt:

  • Large scale data processing. ETL jobs that chew through a few million rows, backfills, nightly aggregations. The kind of thing you’d previously chop into artificial subbatches purely to fit the timeout.
  • Media transcoding. Encoding a long video is not something you can meaningfully checkpoint at minute 14 and resume cleanly.
  • Long running AI inference. Batch inference, embedding generation over a large corpus, or agentic workflows that make many sequential model calls. These routinely blow past 15 minutes and don’t decompose neatly.

For these, 90 minutes isn’t a luxury — it’s the difference between “one clean function” and “an elaborate orchestration you built only to dodge a limit.”

The thesis: Lambda is becoming EC2

Here’s where the wry part lives. Trace the feature creep with me:

  • Timeouts went from 5 minutes, to 15, and now to 90 on Managed Instances.
  • You now pick a capacity provider — C9G vs M9G — which is, let’s be honest, choosing an instance family with a friendlier name.
  • Durable functions give you long lived, resumable state.
  • Async event retention stretches out to a full year.

Squint at that list. Longer running compute, instance family selection, durable state, extended lifecycles. That’s not a list of serverless features. That’s a list of EC2 features wearing a serverless hoodie. The Screaming in the Cloud crowd put it perfectly: Lambda slowly becomes EC2, one feature at a time.

At what point does “serverless” stop being serverless? I don’t think there’s a clean line — it’s a gradient, and we’re sliding down it. And I feel this one personally, because the entire reason Lambda earned my affection is that it hid these knobs from me. Now the knobs are growing back. It’s like watching a friend who moved to the city for the simplicity slowly acquire a lawn, a garage, and opinions about mulch.

The architectural rethink

If you’re a team that’s been fanning long work across Step Functions purely to escape the 15-minute limit, this genuinely warrants a rethink. Some of those state machines exist not because your problem is a workflow, but because the timeout forced you to pretend it was.

So: could you collapse a 40-minute, artificially chunked Step Functions saga into a single 90-minute function? Sometimes, yes. But weigh the tradeoffs honestly:

  • Cost. Lambda bills per millisecond of allocated memory. A single function grinding for 80 minutes at high memory can cost more than a right sized EC2 or Fargate task doing the same work. Scale-to-zero is a gift; long steady state compute is where it stops being one.
  • Observability. A Step Functions graph shows you exactly which step failed. A monolithic 90-minute function is a black box you have to instrument yourself.
  • Retry semantics. If a function fails at minute 85, you rerun the whole thing. Step Functions lets you retry the one step that broke. That granularity is not free to give up.
  • Cold starts. Larger, longer functions with heavier dependencies mean heavier cold starts. For batch work this rarely matters, but know it’s there.

My rule of thumb: if your long job is genuinely one atomic thing (transcode this file, process this dataset), a single 90-minute function is now the cleaner design. If it’s several distinct steps with independent failure modes, keep the orchestrator. Don’t collapse a workflow just because you finally can.

The verdict

Here’s my opinionated take, and I won’t fence-sit: the 90-minute timeout is a genuinely good addition, and it does not change where Lambda actually wins.

Lambda still beats EC2 decisively on the things that made me love it — scale-to-zero, zero patching, per-millisecond billing, and being the best prototyping surface on the planet. Nothing about a longer timeout erodes that. If anything, it removes one of the last “well, actually, you’ll hit the timeout” objections I used to hear in reviews.

But let’s be clear eyed about the trajectory. Lambda is accreting EC2’s shape, and every knob it grows is a small tax on the simplicity that was its whole point. That’s not a criticism so much as a maturation — the service is meeting real workloads where they are. I just hope, selfishly, that the frictionless idea to code path I fell for in the first place stays a first class citizen and doesn’t get buried under capacity providers and instance families.

For now, it’s still the first place my 11pm ideas go. Long may that last.

Where do you draw the serverless line? If you’ve collapsed a Step Functions saga into a single long function — or refused to — I’d love to hear how it went.

I have a favorite AWS service, and it’s not a close race. It’s Lambda.

I’ve said this out loud in enough architecture reviews that people roll their eyes at me. But I mean it, and the reason is embarrassingly simple: Lambda is where my ideas go to become real. When...

Killing the Cold-Start Tax on Serverless AI — Lambda SnapStart for Container Images

Lambda has been my favorite service for years. When I need to stand up a POC or a pilot, nothing beats it — I can deploy code in minutes, wire it to an event, and pay only for what actually runs. So it always stung a little that the standard architecture review for a real time inference endpoint ended the same way: “Lambda would be perfect for this… except cold starts.” Bursty traffic, event driven triggers, pay per use economics — serverless was the obvious fit on paper, and then someone would pull up a P99 latency chart and the conversation moved to a container platform or a provisioned endpoint instead.

That objection just lost most of its teeth — and the timing could not be better. With frontier AI taking off, the workloads I most want to prototype on Lambda are exactly the ones cold starts punished hardest. With Lambda SnapStart now supporting container image functions, the single biggest reason architects steered AI workloads away from Lambda is largely gone — and the design conversation shifts from “how do we survive cold starts” to “how do we design our init phase to be snapshotted.”

Why cold starts are especially brutal for AI

I learned this the hard way. A cold start is Lambda running your initialization code — loading the function, starting the runtime, and executing everything outside the handler — before it can serve the first request. For the plain CRUD functions I’d been happily shipping for years, that’s tens of milliseconds and nobody notices. The first time I dropped a model into a function, I found out that an AI workload is a different universe.

Three things stack up. First, the container image is large. A model runtime plus its transitive dependency tree — think a framework, a tokenizer library, numeric packages, and a CUDA adjacent stack — routinely produces images in the multi gigabyte range. Second, importing those dependencies is expensive: pulling a large ML framework into memory and resolving its native extensions can burn several seconds on its own, before you’ve touched a model. Third, and worst, you load weights at init. Reading a few hundred megabytes of parameters off disk or out of S3 and deserializing them into memory is the dominant cost, and it happens on every cold start.

Add those together and a “warm” invocation that returns in 80 ms is sitting behind a cold start path that takes five to ten seconds. I’ve watched a demo that flew on my laptop fall apart the moment a few concurrent requests forced Lambda to scale out into fresh environments. For a real time inference or agentic tool call, that is not a tail latency nuisance — it is timeouts, blown SLAs, and a genuinely bad user experience the moment traffic spikes. The cold start tax is levied precisely when you can least afford it.

What SnapStart for container images actually is

SnapStart attacks the problem at the mechanism level rather than asking you to shrink your dependencies. When you publish a function version, Lambda runs your function through the entire Init phase once, then takes a Firecracker microVM snapshot of the memory and disk state of that fully initialized environment, encrypts it, and caches it. On a subsequent cold start, Lambda does not re-run your initialization — it restores the microVM from that snapshot and jumps straight to handling the request.

The strategic point for AI workloads: your model load, your framework imports, your client construction all happen once, at publish time, and get frozen into the snapshot. Every future scale-out event restores from that frozen, ready to serve state instead of paying the multi second init bill again.

SnapStart itself is not new — it has been available for zip packaged functions across Java, Python, and .NET. What changed is that it now works for container image functions, and for me that’s the whole story. Containers are how I actually package these workloads. The 250 MB unzipped limit on zip functions and layers was always a poor fit for the gigabyte scale ML stacks I was building; the 10 GB container image support was the natural home. Until now I had to choose: container packaging or snapshot acceleration. I can finally have both.

How to make it work well

Here’s the mental model I’ve settled on. SnapStart rewards a specific architectural discipline: make the init phase do the expensive work, because init is what gets snapshotted.

Concretely, move model loading, dependency imports, and client/session setup to module scope — outside the handler — so they execute during Init and land in the snapshot:

# Runs at Init -> captured in the snapshot
import torch
from my_runtime import load_model

MODEL = load_model("/opt/ml/model")   # heavy: happens once, at publish
MODEL.eval()

def handler(event, context):
    # Warm path only: no model load here
    return MODEL.infer(event["input"])

The catch is uniqueness, and it’s the one that bit me. When Lambda restores many environments from one snapshot, any state created during init is shared across all of them — seeded random number generators, unique IDs, cached credentials, open connections. Freeze a database connection into the snapshot and you will hand every restored environment a stale, possibly closed socket. Seed a PRNG at init and every environment produces the same “random” sequence. I’ve debugged exactly that, and staring at duplicate “random” IDs across invocations is a humbling afternoon.

The fix is runtime hooks. For container images you implement before-snapshot and after-restore hooks so that the runtime coordinates the lifecycle. Use the before-checkpoint hook to gracefully close connections you don’t want frozen, and the after-restore hook to regenerate anything that must be unique: re-seed entropy, refresh temporary credentials, and re-establish network connections. Note the budget — after-restore work counts against a 10-second restore timeout, so keep it lean.

A few more practical levers:

  • Right-size memory. Memory scales CPU on Lambda, and restore plus after-restore work is CPU sensitive. For model serving functions, more memory usually pays for itself in lower restore and inference time.
  • Mind the image and layers. Snapshotting doesn’t excuse a sloppy image. Put model weights and heavy dependencies in stable lower layers, keep your handler code in a thin top layer, and you improve both build hygiene and load behavior.
  • Verify against real deployments. Restore latency depends on snapshot size, so benchmark P99 with your actual model rather than trusting a hello world number.

When it’s the right call — and when it isn’t

I want to be honest about the boundaries, because I’ve talked myself into using Lambda where I shouldn’t have. SnapStart on containers is a strong fit for bursty, spiky inference: workloads that idle then spike, where you’d otherwise overpay for always on capacity or eat cold starts on every scale-out. It’s excellent for agentic tool calls, where an orchestrator fans out to many short lived, independently scaling functions, and for RAG retrieval endpoints that load an embedding model and query a vector store. These are exactly the prototypes I keep reaching for right now.

It is a weaker fit in two cases. For sustained, high throughput inference that keeps environments warm anyway, provisioned concurrency or a dedicated container platform can still win on steady state cost and predictability — SnapStart’s advantage is amortizing init across intermittent cold starts, which matters less when you rarely have one. And for very large models needing GPU acceleration, Lambda is the wrong tool entirely; those belong on dedicated GPU endpoints such as SageMaker. SnapStart accelerates CPU bound init, not the physics of serving a 70B-parameter model.

The takeaway

Serverless AI just got materially more viable, and my favorite service is back on the table for the workloads I care about most. The reflexive “cold starts kill us” objection — the one that ended so many of my architecture reviews — no longer holds for a large and growing class of inference and agentic workloads. That doesn’t mean the work disappears; it moves. The new discipline is designing your init phase for snapshotting: front load the expensive work, handle uniqueness with runtime hooks, and right-size for restore.

  1. Audit your init phase. Everything expensive — model load, imports, client setup — should run outside the handler so it lands in the snapshot.
  2. Handle uniqueness explicitly. Use before-snapshot and after-restore hooks to close/reopen connections, refresh credentials, and re-seed entropy.
  3. Benchmark P99 with your real model, not a toy function, and right-size memory to the restore path.
  4. Pick the fit deliberately — bursty inference and agentic tool calls, yes; sustained high throughput or GPU bound serving, look elsewhere.
  5. Revisit dismissed designs. Endpoints you ruled out over cold starts deserve a second look.

The objection didn’t just weaken — it changed shape. I’m already reopening a couple of “we can’t use Lambda for that” calls I made last year. Which of yours are you ready to revisit?

Lambda has been my favorite service for years. When I need to stand up a POC or a pilot, nothing beats it — I can deploy code in minutes, wire it to an event, and pay only for what actually runs. So it always stung a little that the standard...