When would you choose AWS Lambda instead of ECS, and when would you choose ECS?
Choose Lambda for short, event-driven, bursty work that fits its runtime and packaging constraints and keeps durable state external. Choose ECS for long-running APIs or workers, custom containers, stable processes, and workloads needing explicit CPU, memory, networking, or runtime control. ECS can host stateless or stateful software; critical state should still be externally durable.
How to think about it
The direct answer
I choose AWS Lambda when the work is short-lived, event-driven, independently executable, and naturally scales with the number of events. I choose Amazon ECS when I need a continuously running process, a custom container environment, longer execution, or explicit control over CPU, memory, networking, and deployment.
The boundary is workload shape and operational control, not whether the code happens to be packaged as a container.
Why the boundary exists
Lambda runs a function in response to an invocation such as an HTTP request, S3 event, schedule, or queue message. Each invocation gets an execution environment. AWS may reuse that environment for a later invocation, but that reuse is an optimization, not a guarantee.
That creates a useful model: one event becomes one unit of work. If 1,000 events arrive, Lambda can create many concurrent executions, subject to concurrency quotas and the scaling behavior of the event source. You do not manage servers or keep a process running between requests.
A Lambda invocation has a maximum timeout of 15 minutes. It also runs within Lambda’s supported runtime, memory, filesystem, and networking model. You can use custom runtimes and container images, but a container image does not turn Lambda into ECS. The function still starts because of an invocation and still has the Lambda timeout.
ECS, the Elastic Container Service, schedules and manages containers called tasks. An ECS service maintains a desired number of running tasks, replaces unhealthy tasks, and can put them behind a load balancer. A task can run a server, a queue worker, or a batch process for hours or days, until you stop it or it fails.
ECS itself is the orchestrator, not the physical machine. Its tasks run either on Fargate, where AWS manages the underlying hosts, or on EC2, where you manage the EC2 capacity. Fargate removes server administration, but it does not remove the container and task lifecycle. You still choose the task’s CPU and memory, define health checks, configure networking, and decide how it scales.
| Decision point | Lambda | ECS |
|---|---|---|
| Work shape | Independent events | Long-running services, workers, or jobs |
| Maximum run time | 15 minutes per invocation | Until the task is stopped |
| Runtime control | Managed function environment | Your container and process model |
| Scaling unit | Function concurrency | Tasks and service capacity |
| Best traffic shape | Bursty or irregular | Steady traffic or controlled workers |
| Main operational concern | Timeouts, concurrency, cold starts | Task health, capacity, deployments |
A concrete example
Suppose an online retailer, Northstar, lets sellers upload product images. The system has three jobs:
- Resize each original image into three thumbnails.
- Run a document OCR worker for return receipts.
- Serve the seller dashboard continuously.
The thumbnail pipeline is a good Lambda candidate. An image arrives in S3, an event places a message on SQS, and a Lambda event-source mapping processes the queue. Each image takes 1.8 seconds and uses about 350 MB of memory. On an ordinary day, 10 images arrive per minute. After a marketing email, 20,000 images arrive within five minutes.
This work is independent. Processing image 4 does not require the memory or process state from image 3. The output belongs in S3, and processing status belongs in a durable store such as DynamoDB. Lambda can create concurrency for the queue instead of keeping a fleet of image servers idle all day. At 500 concurrent executions, the idealized worker time for 20,000 images is about 72 seconds, before startup, queue, storage, and downstream-service overhead. Actual completion time depends on quotas and how quickly the event source scales.
The queue matters. It absorbs the burst, gives the system backpressure, and lets Lambda retry failed messages. The handler must be idempotent, meaning running the same message twice produces the same final result rather than two conflicting records. Retries are normal in distributed systems, not evidence that the universe has singled out your function.
The OCR job is different. A receipt can take 22 to 40 minutes, requires a native OCR binary, and keeps a 2 GiB model loaded in memory. Lambda cannot run one receipt for 40 minutes because of its 15-minute limit. Splitting the job into smaller, independently safe stages might make Lambda viable, but if it is naturally one long process, I would run an ECS worker service that polls SQS.
The seller dashboard is also a natural ECS service if it receives steady traffic. For example, five ECS tasks, each configured with 0.5 vCPU and 1 GiB of memory, could sit behind an Application Load Balancer and maintain warm connection pools. The service can scale out when CPU, memory, request count, or queue depth crosses a chosen threshold.
Lambda could serve the dashboard too. That is not automatically wrong. It becomes more attractive when traffic is highly irregular, the handlers are simple, and the latency target tolerates cold-start and initialization behavior. ECS becomes more attractive when the process is steady, initialization is expensive, or the application needs predictable control over its runtime.
The production decision
For Lambda, I normally look for this pattern:
- An event arrives.
- A bounded handler performs one unit of work.
- Durable data lives in S3, DynamoDB, RDS, or another external system.
- A queue, retry policy, and dead-letter queue handle failure.
- Concurrency is capped so a burst does not exhaust the database.
A queue-backed Lambda design still needs capacity planning. Lambda concurrency is not infinite, and a function that scales faster than its database can turn a traffic success into a database outage. I would monitor errors, throttles, duration, queue age, and downstream connection limits. For latency-sensitive functions, provisioned concurrency can reduce cold-start impact, but it adds cost and does not remove every source of latency.
For ECS, I normally look for this pattern:
- A service or worker runs as one or more replaceable tasks.
- An Application Load Balancer routes requests to service tasks, or workers consume a queue.
- Autoscaling changes the task count using a meaningful signal.
- Health checks and deployment rollback prevent bad tasks from replacing good ones.
- Logs, metrics, task exits, and queue age are visible.
Fargate is often the simpler ECS choice when I want containers without managing EC2 hosts. ECS on EC2 is more compelling when I need specialized hardware, unusual host configuration, daemon processes, or better economics at a consistently high level of utilization.
Cost is not a universal Lambda-versus-ECS shortcut. Lambda charges are primarily tied to requests and execution duration, while ECS costs come from running Fargate task resources or maintaining EC2 capacity. Lambda can be economical for 10,000 short invocations and wasteful for a busy, always-on service. ECS can be wasteful for sporadic work if tasks sit idle, but efficient for a stable workload. I would measure the actual request rate, duration, memory, task utilization, and operational burden.
The nuance that earns the senior signal
Neither option makes application state durable.
A Lambda function’s global variables and temporary files may survive a warm reuse, but the next invocation may run in a fresh environment. An ECS task may keep a cache or session in memory while it runs, but ECS can replace that task during a deployment, health-check failure, host problem, or scaling event. User sessions, job progress, and important records belong in durable external systems. A persistent volume solves a different problem and still requires careful failure and concurrency design.
Networking is also not a simple “Lambda is public, ECS is private” distinction. Lambda can run inside a VPC and reach private resources. ECS tasks can run in private subnets as well. The real questions are routing, security groups, startup behavior, connection management, and whether the required network or host configuration fits the service.
The most important scaling difference is the unit being scaled. Lambda scales invocations, while ECS scales tasks. A Lambda burst may create many database connections unless concurrency is controlled. An ECS service may have only ten tasks even while the request rate doubles, because autoscaling takes time and the cluster or Fargate capacity must be available.
A failure mode I would watch for
A common Lambda failure first appears as an SQS backlog growing, followed by Throttles, database connection errors, or repeated message deliveries. The cause is often not that Lambda is “slow”; it is that concurrency has outrun a downstream limit. I would cap concurrency, tune batch size and visibility timeout, make processing idempotent, and add a dead-letter path.
A common ECS failure appears as tasks repeatedly starting and stopping. The ECS service shows a desired count of three but a running count near zero. Task-stop reasons may show a failed health check, missing permission, invalid image, or an out-of-memory termination such as exit code 137. I would inspect stopped-task reasons and container logs before changing the architecture.
What they’ll ask next
Can Lambda use containers?
Yes. Lambda supports container images, but the image must follow Lambda’s invocation model and the 15-minute timeout. Choose that option for packaging convenience, not because it provides ECS-like process behavior.
Is Fargate a replacement for Lambda?
Not exactly. Fargate is managed compute for containers, commonly used by ECS. It is a good fit for long-running APIs, workers, and jobs without EC2 host management. It still starts and charges for tasks, rather than charging per function invocation.
What if a Lambda job takes longer than 15 minutes?
First ask whether it can be split into safe stages and orchestrated with a queue or workflow. If it is one indivisible process, use ECS or another batch-oriented compute option. Increasing Lambda memory may reduce duration, but it cannot remove the maximum timeout.
Say this in the interview
“Lambda is my choice for short, independently retryable, event-driven work with external durable state; ECS is my choice for long-running or steady workloads that need a custom container, warm processes, or explicit resource and networking control, while I cap concurrency or task count to protect downstream systems.”