AWS Lambda for system design interviews
Open in the interactive app: practice, quiz, mock interview →In one minute
- AWS Lambda runs your function in response to events (HTTP via API Gateway or function URLs, SQS, Kinesis, S3, EventBridge, schedules) and bills per invocation and per GB-second, with no servers to manage.
- Each execution environment is a Firecracker microVM running one invocation at a time. Concurrency equals the number of environments busy at once; scaling means creating more environments.
- A new environment pays a cold start (download code, boot the sandbox, start the runtime, run your init code), from roughly 100 ms to several seconds. Warm environments are reused for later invocations.
- Hard limits shape designs: 15-minute timeout, up to 10 GB memory with CPU proportional to it, 6 MB synchronous and 1 MB asynchronous payloads, and a regional concurrency quota (1,000 by default, lower on new accounts) (Lambda quotas).
- Choose it for spiky, event-driven glue and APIs with modest latency needs. Avoid it for long jobs, steady high throughput (containers are cheaper) and paths that cannot absorb cold starts.
How it works
Invocation models
Synchronous invocations (API Gateway, function URLs, SDK Invoke with RequestResponse) wait for the result and return errors to the caller. Asynchronous invocations (S3, SNS, EventBridge, Event type) are queued internally; Lambda retries twice on error, one and then two minutes apart, and can send failures to a destination or DLQ (AWS docs). Event source mappings are Lambda-managed pollers that read SQS, Kinesis, DynamoDB Streams or Kafka in batches and invoke your function synchronously, with retry and checkpoint behaviour defined by the source.
The execution environment lifecycle
An environment goes through Init (start extensions, start the runtime, run code outside the handler), then repeated Invoke phases, then Shutdown when Lambda reclaims it. Between invocations the environment is frozen: background threads stop and timers do not fire. Anything you create during init (SDK clients, database connections, caches in globals, files in /tmp) survives into later invocations on the same environment, which is the main warm-start optimisation.
Concurrency
If a function handles 200 requests per second at 50 ms each, it needs about 200 x 0.05 = 10 concurrent environments. Reserved concurrency caps a function and guarantees it that share of the account quota. Provisioned concurrency keeps a number of environments initialised in advance, removing cold starts for that many concurrent requests at an hourly price. SnapStart (Java, Python, .NET) snapshots an initialised environment and restores new ones from the snapshot instead of running init.
One request per environment is the classic model, now one of several. Lambda Managed Instances (announced December 2025) run functions on EC2 instances in your account, isolated by containers on Nitro rather than Firecracker, and let one environment serve many invocations at once; they scale on CPU without cold starts and cost the EC2 price plus a 15% management fee (AWS docs). Durable functions checkpoint each step and replay after interruptions, so one workflow can run for up to a year and wait without paying for compute (AWS docs). AWS now calls the classic kind Event Functions, next to Web Functions, which run an ordinary HTTP server with 64 concurrent requests per environment by default (Lambda quotas).
Down the stack
Layer 1The contract
Lambda promises at-least-once execution for asynchronous and event-source invocations: retries and internal redelivery mean your handler can run more than once for the same event, so handlers must be idempotent. A synchronous invoke runs at most once per request from Lambda's side, but client retries can repeat it. Each environment handles one request at a time, so per-environment code needs no locking. Functions scale until they hit the account or reserved concurrency limit, after which synchronous calls get 429 TooManyRequestsException and asynchronous events are retried for up to 6 hours. Scaling speed is bounded too: each function can add 1,000 concurrent environments every 10 seconds, refilled continuously and never banked (AWS docs). Warm invocation overhead is typically a few milliseconds; cold starts add roughly 100 ms to over 1 s depending on runtime, package size and init work.
Layer 2The mechanism
Routing. A front-end fleet authenticates the request and asks an assignment service for an idle environment of that function version. If one exists, the request goes to its worker; if not, a placement service picks a worker with capacity and creates one. AWS has described rebuilding assignment around a replicated log so environment state survives failures (hedge: from re:Invent talks, not formal docs).
Workers and Firecracker. Workers are bare-metal EC2 hosts running many Firecracker microVMs. Firecracker is a Rust virtual machine monitor on KVM with a minimal device model (virtio net and block, serial console), booting a guest kernel in under 125 ms with under 5 MiB of overhead per microVM, and up to 150 microVMs per second per host (Firecracker). Each microVM holds a guest kernel, the runtime and your code for one function in one account, giving VM-level isolation between tenants.
Code delivery. Zip packages (50 MB zipped, 250 MB unzipped) and container images (10 GB) are cached near workers. Image layers are split into chunks, deduplicated across customers with convergent encryption, and loaded lazily, so a 1 GB image need not download fully before start (Brooker et al., USENIX ATC 2023).
Runtime API. The runtime loops on GET /2018-06-01/runtime/invocation/next, calls your handler, then POSTs to /runtime/invocation/<request-id>/response (or /error). Custom runtimes plug in here. For SQS, Lambda-managed pollers long-poll, batch, invoke and delete on success, scaling with backlog up to the mapping's maximum concurrency.
Layer 3The machine
Memory is the single sizing knob: CPU is allocated in proportion, reaching one full vCPU at 1,769 MB and up to 6 vCPUs at 10,240 MB. A CPU-bound function at 512 MB gets about 0.3 of a vCPU, so doubling memory can halve duration at the same cost. Billing is per millisecond of duration times configured memory, $0.0000166667 per GB-second on x86 and $0.0000133334 on Arm (20 percent less) in the cheapest tier, plus $0.20 per million requests (AWS pricing).
A cold start splits into sandbox creation (tens of ms, with pre-created pools), code loading (proportional to bytes touched), runtime start (a few ms for Node or Python, hundreds for a JVM) and your init code, often the largest part. Init is capped at 10 seconds for on-demand functions (AWS docs). Since 1 August 2025 that init time is billed for every configuration; before, on-demand zip functions on managed runtimes got it free (AWS Compute Blog). SnapStart instead restores memory pages from a snapshot on demand, so code assuming uniqueness at init (random seeds, connections) needs care.
Idle environments are reclaimed after an unspecified period, commonly observed as minutes. VPC-attached functions use shared Hyperplane ENIs, so VPC attachment no longer adds seconds to cold starts as it did before 2019 (AWS Compute Blog).
Why it's built this way
- Customers wanted to pay only for actual work, so capacity is allocated per request rather than per server, which requires creating and destroying sandboxes quickly.
- Running untrusted code from many accounts on shared hosts requires VM-grade isolation, but traditional VMs boot in seconds and cost hundreds of MB, so AWS built Firecracker to make VMs as cheap as containers.
- One request per environment keeps the programming model simple and isolation clean, at the cost of more environments for concurrent load.
- Cold starts are the price of not reserving capacity, so Lambda reuses warm environments, and offers provisioned concurrency and SnapStart for those who will pay to avoid them.
- Fleet stability needs limits, so concurrency quotas, scaling rates and timeouts protect both AWS and downstream systems from runaway fan-out.
Diagram
sequenceDiagram
participant C as Caller
participant F as Front end
participant A as Assignment service
participant P as Placement
participant W as Worker (Firecracker microVM)
C->>F: Invoke
F->>A: need env for fn:v3
alt warm env idle
A-->>F: env 17 on worker W
else none idle
A->>P: create env
P->>W: boot microVM, load code, run init
W-->>A: env ready (cold start)
A-->>F: new env on W
end
F->>W: event via runtime API (/invocation/next)
W-->>F: POST /response
F-->>C: resultWorked example
An image-thumbnail service receives S3 upload events: 2 million images a day, with a launch-day spike of 3,000 uploads per second for 10 minutes. Each thumbnail takes 400 ms at 1,024 MB.
Normal load averages about 23 per second, needing about 23 x 0.4 = 9 concurrent environments. The spike needs 3,000 x 0.4 = 1,200, above the default regional quota of 1,000, and that quota is shared with every other function in the account. The team requests a quota increase to 3,000 and sets reserved concurrency of 1,500 on the thumbnail function so it cannot starve the customer-facing API. Because S3 events invoke asynchronously, any throttled events are retried by Lambda for up to 6 hours, so the spike causes delay rather than loss.
Cost: 2 million x 0.4 s x 1 GB = 800,000 GB-seconds a day, about $13. Containers sized for the peak would sit mostly idle, so Lambda wins. At a steady 3,000 per second all day the same math gives about $1,700 a day, where containers would cost a fraction.
Python with Pillow cold-starts in about 300 ms; about 1,200 environments start cold once during the spike, then are reused. Nobody waits synchronously on thumbnails, so no provisioned concurrency is needed.
In the wild
- AWS, 2023: a Lambda front-end cell crosses an untested size. On 13 June the Lambda Frontend fleet in us-east-1 scaled up for normal growth, and at 11:49 PDT one cell passed a capacity threshold it had never reached, waking a latent bug: new execution environments were allocated but never used. Synchronous invocations recovered by 13:45 and everything by 15:37 PDT, while STS, console sign-in, EventBridge and EKS provisioning, all built on Lambda, were degraded. AWS fixed the bug and committed to keeping cells within well-tested sizes (AWS).
- Amazon Prime Video, 2023: a serverless pipeline moved into one process. A stream-quality monitor built from Step Functions, Lambda and S3 hit a hard scaling limit at about 5% of expected load: each second of stream needed several state transitions, which exhausted account limits and were billed one by one, and passing frames through S3 was expensive. Packing the same components into one ECS service cut infrastructure cost by over 90% (Prime Video Tech, archived). It is this lesson's cost warning at steady high throughput, in production.
Trade-offs
| Option | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| Lambda | No servers, per-ms billing, scales from zero | Cold starts, 15-minute limit, cost at steady high load | Spiky, event-driven work and glue |
| Lambda + provisioned concurrency | Predictable latency | Pays for idle capacity | Latency-sensitive APIs with known baseline |
| ECS Fargate or Cloud Run | Containers, long-running, no host management | Slower scaling (tens of seconds), per-task overhead | Steady services, long jobs |
| EC2 or EKS | Cheapest at steady load, full control | You run capacity and patching | Large steady workloads, special hardware |
| Step Functions + Lambda | Orchestration beyond 15 minutes with retries | More moving parts, per-transition cost | Multi-step workflows across many services |
| Lambda durable functions | Workflows in plain code for up to a year, free while waiting | Code outside steps re-runs on every replay; newer | Multi-step logic that belongs inside one function |
| Lambda Managed Instances | Many requests per environment, EC2 pricing and hardware, no cold starts | Always-on minimum instances, 15% fee, handlers must be thread-safe | High steady volume that should keep Lambda's event integrations |
Pitfalls in production
- Database connection storms. 1,000 environments can exhaust a relational database. Use RDS Proxy and cap concurrency.
- Runaway recursion. A function writing to the bucket that triggers it invokes itself forever. Separate buckets or prefixes. Lambda now stops loops through SQS, SNS, S3 and EventBridge after about 16 invocations in one chain, but only with supported SDKs (AWS docs).
- Shared account quota. A batch job can throttle the user-facing API. Partition with reserved concurrency.
- Work after returning. The environment freezes, so background flushes stall. Flush before returning.
- Timeouts shorter than SDK retries. The function dies mid-retry. Align SDK timeouts with the function timeout.
Leadership lens
- Ask for the concurrency math (requests per second times duration) and compare it with account quotas and downstream limits before approving a Lambda design.
- Model cost at expected steady load, not just at launch; Lambda is cheapest for spiky or low-volume work and expensive for constant high throughput.
- Standardise idempotency, structured logging, timeouts and DLQs or failure destinations in a shared template so every team gets them by default.
- Decide which paths are latency critical and budget for provisioned concurrency or SnapStart only there.
Check yourself
Why does increasing a function's memory sometimes reduce its cost?
Show answer
CPU scales with memory, so a CPU-bound function finishes proportionally faster and GB-seconds stay flat or fall while latency improves.
A function serves 500 requests per second at 80 ms each. How many concurrent environments does it need, and what happens at the default quota?
Show answer
About 40, well within a 1,000 quota; at 100 times that load it would need 4,000 and be throttled with 429 errors.
Why can global variables be both useful and dangerous in a Lambda handler?
Show answer
They persist on a warm environment, enabling connection and cache reuse, but can leak per-request data between users and are not shared across environments.
Why must an SQS-triggered function be idempotent even if it never fails?
Show answer
SQS and the poller provide at-least-once delivery, so a message can be delivered twice, for example when a visibility timeout expires during a slow batch.
Sources and further study
Primary
- Wang et al., Peeking Behind the Curtains of Serverless Platforms (USENIX ATC 2018): an outside measurement of Lambda, Azure Functions and Google Cloud Functions across 50,000 instances, covering placement, cold starts and contention.
- Brooker et al., On-demand Container Loading in AWS Lambda (USENIX ATC 2023): how 10 GB images start in seconds through chunking, deduplication and lazy loading.
- Jonas et al., Cloud Programming Simplified: A Berkeley View on Serverless Computing (2019): the limits of FaaS for state, communication and long jobs.
Systems docs
- Lambda Managed Instances: the multi-concurrency compute type on EC2 and how it differs from the default.
- Lambda durable functions: checkpoint and replay for workflows up to a year long.
- AWS Builders' Library: Avoiding insurmountable queue backlogs: how AWS reasons about asynchronous queues like the one behind async invokes.
Books
- Designing Distributed Systems, 2nd edition, Brendan Burns, chapter 9 "Functions and Event-Driven Processing": when FaaS makes sense, and patterns for it.
- Systems Performance, 2nd edition, Brendan Gregg, chapter 11 "Cloud Computing": hardware, OS and lightweight virtualization, the spectrum Firecracker sits on.
Other curricula
- AWS Well-Architected: Serverless Applications Lens: frames Lambda designs as common serverless scenarios, each reviewed against the Well-Architected pillars rather than as a feature tour.
Watch
- AWS re:Invent 2018: A Serverless Journey: AWS Lambda Under the Hood (AWS): the front end, worker manager, placement and Firecracker workers behind an invoke.
- AWS re:Invent 2019: Scalable serverless event-driven applications using Amazon SQS & Lambda (AWS Events): how Lambda's SQS pollers scale, batch and handle failures.
Free resources
- Lambda execution environment lifecycle: the Init, Invoke and Shutdown phases and freezing between invokes.
- Understanding Lambda function scaling: concurrency, scaling rate and how throttling works.
- Configuring provisioned concurrency: pre-initialised environments and their cost.
- Lambda quotas: memory, timeout, payload, package and concurrency limits.
- Improving startup performance with Lambda SnapStart: snapshot restore and its uniqueness caveats.
- Firecracker: Lightweight Virtualization for Serverless Applications (NSDI 2020): the paper on the microVM monitor that runs Lambda.