Lambda for spiky, event-driven, short-lived work; Fargate for long-running services with steady concurrency. For most workloads, that one line settles the debate in a minute. The hard cases live in the grey zone: APIs with moderate but constant traffic, jobs that flirt with the 15-minute ceiling, functions that have quietly grown into monoliths. This article is specifically about the container-versus-function line; for the broader question of leaving servers behind at all, see our Lambda vs servers guide.
Where does Lambda win outright?
Lambda wins when traffic is spiky or unpredictable, when work arrives as discrete events, when each request should be isolated from its neighbours, and when you want operations to round to zero. It scales from nothing to thousands of concurrent executions without a capacity plan, and it costs nothing at rest.
The event-driven fit is structural, not cosmetic. S3 uploads, SQS batches, EventBridge rules and DynamoDB streams plug into functions natively, with retries and batching handled by the platform. Per-request isolation also limits blast radius: a poison message or a memory leak takes down one execution environment, not a shared process serving hundreds of connections. If your workload is a set of short, independent reactions to events, Lambda is not just cheaper at low volume, it is less code and fewer moving parts to operate.
Where does Fargate win outright?
Fargate wins for long-running processes, steady concurrency, WebSockets and anything that holds connections open, jobs that run past 15 minutes, and resource shapes Lambda cannot express. A container that needs many vCPUs with modest memory, or memory far beyond Lambda's ceiling, has exactly one serverless home: a Fargate task.
Lambda ties CPU to memory on a single slider and caps execution at 900 seconds, limits documented in the Lambda quotas page. Fargate lets you pick vCPU and memory independently, currently up to double-digit vCPUs and roughly an order of magnitude more memory than Lambda offers. Image size is also freer: no layer juggling, just a container pulled from your registry, as described in the Fargate documentation. Long-lived worker pools that drain a queue at a steady rate all day are the canonical case.
What does a Lambda that wants to be a container look like?
The migration smell is a function configured with a 900-second timeout, a stack of layers fighting the packaging model, and an internal router dispatching dozens of routes inside one handler. That is a container runtime being reimplemented on top of a function platform, and it inherits the constraints of both.
Other symptoms we look for: state smuggled through /tmp between invocations, provisioned concurrency dialled up so high the bill looks like a server anyway, and orchestration hacks to chain invocations past the duration limit. None of these are bugs; they are signals that the workload's natural shape is a process, not a reaction. Moving it into a container on Fargate usually deletes code: the router, the timeout gymnastics and the layer management all go away.
Stuck in the grey zone with a Lambda that keeps hitting its limits? Describe your system: a one-page diagnosis within 48 hours.
Get my diagnosis →How do the cost shapes cross over?
Lambda bills per request plus per unit of memory-time while your code actually runs, and nothing at rest. Fargate bills per vCPU-hour and per GB-hour for as long as the task exists, whether it is serving traffic or not. The crossover therefore hinges on sustained utilization.
A function that is busy most of every hour is paying Lambda's premium per-millisecond rate for close to the whole hour, at which point a task billed at Fargate's flat hourly shape tends to come out ahead. A workload that is idle 95 percent of the time inverts the logic completely. Where exactly the line falls depends on memory configuration, region, architecture and discounts, so we run the numbers per workload against the official Lambda pricing and Fargate pricing pages rather than trusting rules of thumb.
How do cold starts compare to task starts?
They differ by roughly an order of magnitude, and they land in different places. A Lambda cold start typically costs somewhere between tens of milliseconds and a few seconds, paid on the request path. A Fargate task start, image pull included, commonly takes tens of seconds and sometimes minutes.
The consequence matters more than the numbers, which vary by runtime, image size and region. Lambda absorbs a traffic burst in seconds but makes some requests wait; Fargate scale-out is too slow to chase a spike, so you hold headroom instead, which is exactly the always-on capacity you were trying to avoid. If cold starts are your main objection to Lambda, they are often fixable before they justify a migration: our guide to keeping Lambda functions warm covers the options.
Does the choice change your front door?
Usually not, and that is worth exploiting. Both Lambda functions and Fargate tasks register behind the same Application Load Balancer as target groups, both consume the same SQS queues, and both subscribe to the same EventBridge buses. The traffic layer is not what this decision is about.
This symmetry makes the grey zone less scary. Keep handlers thin, keep business logic in plain modules, and the cost of being wrong drops to packaging and deployment wiring. We have moved workloads in both directions behind an unchanged ALB listener rule, and the callers never noticed. Treat the compute choice as reversible and it stops being a bet.
Fargate vs Lambda at a glance
The table below compresses the decision. Read the "when it wins" row first, then check the constraint rows against your workload: duration limits, resource shape and sustained utilization are the three criteria that most often force the answer on their own, before anything else gets a vote.
| Lambda | Fargate | |
|---|---|---|
| Startup | Cold start: milliseconds to a few seconds | Task start: tens of seconds to minutes |
| Duration limits | Hard cap at 15 minutes | None: tasks run as long as needed |
| Scaling model | Per request, from zero, platform-managed | Per task, via service auto scaling policies |
| Cost shape | Per request plus memory-time while running | Per vCPU-hour and GB-hour while the task exists |
| Ops | Near zero: no images, no orchestrator | Low: images, task definitions, scaling policies |
| When it wins | Spiky, event-driven, short-lived, isolation-sensitive work | Long-running, steady load, WebSockets, large CPU/memory shapes |
The decision checklist
Five questions settle most grey-zone cases:
- Does any single unit of work exceed 15 minutes? Fargate.
- Is the workload busy the majority of every hour? Cost out the Fargate shape.
- Do you hold connections open (WebSockets, streaming)? Fargate.
- Is traffic spiky with real idle periods? Lambda.
- Is your Lambda already fighting timeouts, layers or an internal router? That is your migration signal.