AWS Lambda & Serverless
Running code without managing any servers at all — Lambda's execution model, triggers, and when serverless beats EC2.
Want a visual for this topic?
Generate a diagram tailored to AWS Lambda & Serverless — the AI picks whichever visual (architecture, flowchart, ER diagram, etc.) best fits this specific AWS concept.
Sign in to generate a visual →🎓 Learning objectives
- •Explain what 'serverless' actually means (servers still exist — you just don't manage them)
- •Explain Lambda's pricing model and how it differs from EC2's
- •Name at least 4 common Lambda trigger sources
- •Explain cold starts and when they matter
What is it?
AWS Lambda lets you run code in response to events — an HTTP request, a file uploaded to S3, a message on a queue, a scheduled time — without provisioning, managing, or paying for any server that sits idle. You upload your function's code, AWS handles everything about running it: the underlying compute, scaling from zero to thousands of concurrent invocations and back, and patching the execution environment.
Why it exists
Even with EC2 Auto Scaling, you're still managing a fleet of always-running (or at-minimum-count-running) servers, patching their OS, and paying for them continuously even during genuinely idle periods — a minimum of 1-2 instances running 24/7 for a service that only actually receives traffic a few times a day. Lambda exists to remove that entire layer of concern for workloads that are naturally event-driven and bursty: the code only runs (and you only pay) exactly when there's an actual event to respond to.
Problem it solves
It solves the idle-capacity-cost problem (zero cost when there's no traffic — a genuine scale-to-zero model EC2 Auto Scaling can't match since even a minimum of 1 instance is always running), the server-management problem (no OS patching, no capacity planning, no health checks to configure), and the fine-grained-scaling problem (Lambda can scale from 0 to thousands of concurrent executions within seconds, far faster than EC2 instances can boot).
Intuition
Think of the difference between owning a car (an always-available EC2 instance, costing money whether you drive it or not) and calling a taxi only exactly when you need a ride (a Lambda invocation, paid only for that specific trip, with zero cost the rest of the time). The taxi model is perfect for occasional, unpredictable trips; it becomes expensive and impractical if you actually need to be driving constantly all day, where owning the car is more economical.
Analogy
A vending machine versus a staffed snack counter: the staffed counter (EC2) costs money to keep open regardless of whether anyone's buying (an employee's wage runs whether there are customers or not), while a vending machine (Lambda) has zero ongoing cost when idle and 'activates' (dispenses a snack, runs your code) only in response to an actual event (a coin inserted, an HTTP request received), scaling to as many simultaneous vending machines as needed without you hiring more staff.
Technical explanation
When an event triggers a Lambda function, AWS provisions an isolated execution environment (a lightweight, secure micro-VM using AWS's Firecracker technology), loads your code, and runs your designated handler function. If no warm execution environment already exists for this function, this provisioning step causes a 'cold start' — extra latency (commonly 100ms to a few seconds, depending on runtime and package size) before your code actually begins executing; a 'warm' invocation, reusing an environment from a recent previous invocation, skips this and starts executing in single-digit milliseconds. Lambda charges based on the number of invocations plus the compute time actually used, measured in GB-seconds (memory allocated × execution duration) — there is no charge at all when the function isn't running, a fundamentally different billing model from EC2's per-second-while-running-regardless-of-load pricing.
Architecture
A typical serverless API: API Gateway receives HTTP requests and routes them to a Lambda function per route/method; the Lambda function executes business logic and reads/writes to DynamoDB (a serverless-friendly database with the same pay-per-use, no-idle-cost model); S3 triggers a separate Lambda function whenever a new file is uploaded (e.g. to generate a thumbnail); an EventBridge scheduled rule triggers a nightly cleanup Lambda function. None of these components require you to provision or manage a single server.
Workflow
- Write your function code in a supported runtime (Node.js, Python, Java, Go, .NET, Ruby, or a custom container image). 2) Define the handler (the entry-point function AWS calls with the event payload). 3) Configure the trigger (what event source invokes this function). 4) Set memory allocation (which also proportionally determines CPU allocation) and timeout. 5) Attach an IAM execution role scoped to exactly what the function needs to access. 6) Deploy — AWS handles the rest, including scaling concurrent invocations automatically as event volume changes.
Example
An image-sharing app uses a Lambda function triggered by S3 'object created' events: whenever a user uploads a photo, S3 fires an event, Lambda automatically spins up, resizes the image into several thumbnail sizes, writes them back to S3, and then the execution environment is torn down (or kept warm briefly for the next similar event) — during a burst of 500 simultaneous uploads, Lambda scales to roughly 500 concurrent executions automatically, and during a quiet overnight period with zero uploads, the function costs exactly $0, with no idle server sitting around waiting for the next photo.
Real-world usage
Lambda underlies huge swaths of event-driven backend logic across AWS customers — from image/video processing pipelines to API backends to data pipeline glue code connecting other AWS services — specifically because so much backend logic is naturally 'do a small amount of work in response to a specific event' rather than 'run continuously.' AWS itself uses Lambda internally for parts of its own service infrastructure.
Trade-offs
Lambda trades operational simplicity and true scale-to-zero cost for less control over the execution environment (no persistent local state between invocations by default, a hard execution time limit, cold-start latency) and, at sustained high volume, potentially higher cost than an equivalently-sized always-running EC2 fleet. The general rule of thumb: Lambda wins for event-driven, bursty, or infrequent workloads and for teams wanting minimal operational burden; EC2/containers win for constant high-throughput workloads, long-running processes, or when you need more control over the runtime environment.
Visual explanation
Picture an event source on the left (an API Gateway request, an S3 object-created event, an SQS message, a CloudWatch scheduled rule) with an arrow pointing to a Lambda function icon in the middle, and an arrow leaving it toward whatever the function does (write to DynamoDB, call another API, send a notification). Unlike an EC2-based diagram, there's no 'always-on box' drawn for the compute — the Lambda function only 'exists' as a running process for the milliseconds-to-minutes it takes to handle one invocation, then disappears.
Advantages
- —
True scale-to-zero — zero cost when there's no traffic, unlike EC2 Auto Scaling's minimum-instance-count floor
- —
No server management at all — no OS patching, no capacity planning, no health check configuration
- —
Scales to very high concurrency automatically and quickly, without needing to pre-provision for a spike
- —
Fine-grained, usage-based billing (per invocation + execution time) instead of paying for idle time
Disadvantages
- —
Cold starts add latency unpredictably, which matters for latency-sensitive synchronous request paths (though provisioned concurrency can mitigate this at extra cost)
- —
Maximum execution duration (15 minutes) makes Lambda unsuitable for long-running processes — those need EC2, ECS, or Step Functions orchestration instead
- —
Can become more expensive than EC2/containers for sustained, constant, high-volume traffic, where an always-running instance's flat cost beats per-invocation billing at high enough volume
- —
Debugging and local testing are less straightforward than a normal server process — you're working within AWS's execution model and event/response contract
Common mistakes
- —
Using Lambda for long-running batch jobs that exceed or approach the 15-minute execution limit, instead of using Step Functions to orchestrate multiple shorter Lambda invocations or moving that specific job to ECS/EC2
- —
Not accounting for cold-start latency in a user-facing, latency-sensitive synchronous API path, and being surprised by inconsistent response times
- —
Granting an overly broad IAM execution role 'to make it work' instead of scoping it to exactly the resources that specific function needs (the same least-privilege principle as EC2 roles)
- —
Assuming Lambda is always cheaper than EC2 without actually modeling cost at your expected invocation volume — at very high sustained request rates, EC2/containers can be the cheaper option
In the AWS Console
- 1
AWS Console → Lambda → Create function
Choose 'Author from scratch', name the function, select a runtime (e.g. Python 3.12), and let AWS create a basic execution role, or attach an existing one.
The default auto-created execution role only grants CloudWatch Logs write access — you'll need to add specific permissions (e.g. S3, DynamoDB) for the function to interact with other services.
- 2
Lambda → [your function] → Code tab
Edit the code inline for small functions, or upload a .zip package / container image for anything with dependencies.
The inline editor is fine for quick tests, but any real project should use a proper build/deploy pipeline (SAM, CDK, Serverless Framework) rather than console-editing production code.
- 3
Lambda → [your function] → Configuration → Triggers → Add trigger
Choose an event source — e.g. 'S3', specifying the bucket and event type ('All object create events') — to wire up the function to fire automatically.
Each trigger type has its own event payload shape; check AWS's documented event structure for that specific trigger before writing your handler's parsing logic.
- 4
Lambda → [your function] → Configuration → General configuration → Edit
Set Memory (which also scales CPU/network proportionally) and Timeout appropriate to the workload — starting too low causes timeouts/out-of-memory errors, too high wastes money per invocation.
Use CloudWatch metrics from real invocations (Duration, Memory Used) to right-size these values after initial testing, rather than guessing once and leaving it.
🎤 Interview questions
What does 'serverless' actually mean — are there really no servers involved? (Listen for: servers still exist physically, you simply don't provision, patch, or manage them — AWS handles that layer entirely.)
How does Lambda's pricing model differ from EC2's? (Listen for: Lambda charges per invocation + actual execution time/memory used, zero cost when idle; EC2 charges per second while the instance is running, regardless of whether it's handling traffic.)
What is a cold start, and when does it matter most? (Listen for: extra latency provisioning a new execution environment for the first invocation in a while; matters most for latency-sensitive synchronous request paths, less for async/background processing.)
When would you choose EC2 or containers over Lambda for a given workload? (Listen for: long-running processes beyond 15 minutes, sustained high-constant-throughput workloads where always-on pricing beats per-invocation pricing, or needing more control over the runtime environment.)
Name three different ways a Lambda function can be triggered. (Listen for: any combination of API Gateway/HTTP, S3 events, SQS/SNS messages, DynamoDB Streams, EventBridge scheduled rules, direct SDK invocation.)