AWS Lambda Explained: How It Works, When to Use It, and Key Limits

AWS Lambda is a serverless compute service that runs your code in response to events: an HTTP request, a file uploaded to S3, a scheduled timer, or a message arriving in a queue. You write a function, upload it to AWS, and Lambda handles everything else — provisioning, scaling, and patching. You pay only when your code is executing, billed per request and per millisecond of runtime.

Lambda is built for short-lived, stateless, event-driven work. A concrete example: a user uploads a profile photo to your application. An S3 event triggers your Lambda function, which resizes the image and saves the result. No server to maintain, no idle cost while you wait for uploads to arrive. That is the Lambda model.

It is not the right fit for everything. Lambda has a 15-minute maximum runtime, cannot keep in-memory state between invocations, and at sustained high throughput costs more than a reserved EC2 instance. Understanding those boundaries is as important as knowing what Lambda is good at.

AWS Lambda in simple terms

Lambda is a function runner. You write a function in Python, Node.js, Java, or another supported language, configure what should trigger it, and deploy it. When the trigger fires, Lambda runs your function. Once or a thousand times simultaneously, it scales to match.

You never think about the server. You never configure auto-scaling rules or provision capacity in advance. If zero requests arrive, nothing runs and nothing costs money. If 5,000 requests arrive at once, Lambda runs 5,000 instances of your function in parallel, subject to your account’s concurrency limit.

Compare that to EC2: a virtual machine that runs continuously, whether it is processing requests or sitting idle. Lambda is the opposite — it exists only during execution.

Why Lambda matters

The core appeal is reduced operational work. No patching OS images, no capacity planning, no load balancer configuration for scaling. Teams that want to ship features rather than manage infrastructure find Lambda compelling for the right workloads.

Lambda also scales to zero. If your function gets no traffic, it costs nothing. This makes it genuinely cost-effective for workloads with sporadic, bursty, or unpredictable usage: a webhook receiver, a nightly report job, or a rarely-triggered automation.

The trade-off is that Lambda is narrow in scope by design. It does not replace EC2 or containers. It is a tool for a specific kind of work: short, stateless, event-triggered execution.

How AWS Lambda works

Lambda runs code in execution environments: isolated, ephemeral containers managed by AWS. Here is the lifecycle from trigger to response:

  1. A trigger fires: an HTTP request via API Gateway, a file uploaded to S3, a scheduled rule from EventBridge, a message from SQS, and so on.
  2. Lambda looks for a warm execution environment (one already initialised from a previous invocation). If none is available, it creates a new one — a cold start.
  3. Lambda passes the event payload to your function’s handler.
  4. Your handler runs and returns a response (for synchronous invocations) or finishes silently (for asynchronous ones).
  5. The execution environment stays warm for several minutes, ready for the next invocation.
  6. If no invocations arrive, Lambda eventually freezes and terminates the environment.

The handler is the entry point Lambda calls. A minimal Python example:

def handler(event, context):
    # event: dict containing the trigger's data
    # context: runtime info — function name, time remaining, request ID, etc.
    name = event.get('name', 'World')
    return {
        'statusCode': 200,
        'body': f'Hello, {name}!'
    }

The event object varies by trigger source. An API Gateway event includes queryStringParameters, headers, and body. An S3 event includes the bucket name and object key. Your function reads from event and returns a result.

Any code outside the handler runs once at cold start and is reused across warm invocations. Initialise SDK clients and database connections at module level, not inside the handler, to avoid re-creating them on every call.

Supported runtimes

Lambda provides managed runtimes that AWS keeps patched and updated. You can also bring a custom runtime using provided.al2023, or package your function as a container image for maximum flexibility.

LanguageManaged runtimes
Python3.9, 3.10, 3.11, 3.12
Node.js18.x, 20.x
Java11, 17, 21
Go1.x (provided.al2023)
.NET / C#6, 8
Ruby3.2, 3.3
Custom runtimeprovided.al2023: bring your own binary
Container imageAny language, up to 10 GB image stored in ECR

Common Lambda triggers and real-world use cases

Lambda connects to almost every AWS service as a trigger source. For a deeper look at invocation patterns, see the event-driven compute guide.

Trigger sourceWhat it enablesExample use case
API Gateway / Function URLHTTP endpoints, REST APIsLightweight API backend, webhook receiver
S3 eventsReact to object uploads or deletionsImage resizing, virus scanning, ETL triggers
EventBridge (scheduled)Cron-style scheduled jobsNightly report generation, cleanup tasks
SQS queueProcess messages from a queueOrder processing, email dispatch, async task runners
SNS topicFan-out event handlingNotification pipelines, multi-system updates
DynamoDB StreamsReact to table row changesReal-time aggregation, search index updates
Kinesis Data StreamsProcess streaming data recordsClickstream analysis, IoT data ingestion
EventBridge rulesReact to AWS service events or custom eventsAuto-remediation, cross-service automation

When to use Lambda

  • Short-lived workloads: Functions that complete within seconds or a few minutes. Lambda’s 15-minute maximum timeout covers the vast majority of event-driven tasks.
  • Stateless execution: Each invocation is independent. Any state lives in DynamoDB, S3, or another external service, not in the function itself.
  • Bursty or unpredictable traffic: Lambda scales instantly from zero to thousands of concurrent executions with no provisioning. See Lambda scaling behaviour for how concurrency works.
  • Event-driven automations: Glue logic between AWS services: reacting to uploads, database changes, queue messages, or scheduled triggers.
  • Lightweight API backends: REST APIs or webhooks with variable traffic and no need for persistent connections.
  • Scheduled jobs: Cron-style tasks triggered by EventBridge: nightly reports, cleanup, cache warming.

When not to use Lambda

  • Long-running jobs: Lambda has a hard 15-minute timeout. Use AWS Batch or ECS tasks for workloads that run for hours. Chaining Lambda calls to work around the limit is fragile.
  • Stateful applications: Lambda cannot reliably keep in-memory state between invocations. Use ECS or EC2 for services that maintain connections or session data.
  • Sustained high-throughput workloads: At consistently high invocation rates, Lambda’s per-request billing exceeds the cost of a reserved EC2 instance or Fargate task. See Lambda vs EC2 for the comparison.
  • Full OS or runtime control: If you need specific kernel settings, custom installed software, or a non-standard runtime, Lambda’s managed execution model is too restrictive. Use EC2 or a container.
  • Strict cold-start-sensitive latency: Cold starts introduce variable latency. If your SLA requires consistent sub-10ms p99 response times, Lambda without provisioned concurrency is a risky choice.
  • Persistent database connections at scale: Lambda’s ephemeral, scale-to-zero model creates too many short-lived connections for traditional databases. Use RDS Proxy or a connection pooler in front of your database.

Lambda vs EC2 vs containers

The right choice depends on runtime duration, traffic patterns, and how much operational overhead you want. For a complete decision framework, see the Lambda vs ECS vs EC2 guide and the dedicated Lambda vs EC2 comparison.

FactorLambdaEC2Containers (ECS / EKS)
Max runtime15 minutesUnlimitedUnlimited
Traffic patternBursty, variable, zero-to-highSustained, predictableSustained or bursty
StateStateless onlyStateful or statelessStateful or stateless
Ops overheadMinimal: no servers to manageHigh: patching, sizing, scalingMedium: container orchestration
Cost modelPer invocation + per ms of executionPer hour (on-demand or reserved)Per vCPU/memory (Fargate) or EC2 hours
Cold start riskYes, mitigated with provisioned concurrencyNoneContainer start-up time only
Best fitEvent-driven tasks, automations, short APIsLong-running services, custom OS needsMicroservices, long-lived API servers

See also: Lambda vs ECS and serverless vs virtual machines.

Cold starts explained simply

Cold start: When Lambda has no warm execution environment available, it creates a new one. This involves downloading your code package, initialising the runtime, and running any code outside your handler. This adds latency, typically 100ms to 1 second for Python and Node.js, and potentially several seconds for Java or large dependency packages.

Warm start: If a previous execution environment is still available, Lambda reuses it. The runtime is already initialised. Your handler runs immediately. Latency is just your code’s execution time.

Cold starts matter for user-facing APIs where consistent response time is expected. For background processing, scheduled jobs, and event-driven pipelines, cold starts are usually irrelevant.

Practical ways to reduce cold start impact:

  • Keep packages small: Smaller deployment packages initialise faster. Remove unused dependencies.
  • Initialise outside the handler: SDK clients, database connections, and loaded models at module level are created once at cold start and reused on warm invocations.
  • Use provisioned concurrency: Pre-warm a specific number of environments so they are always ready. Costs extra; only enable it for functions where cold start latency is genuinely unacceptable. Covered in detail in the Lambda scaling guide.
  • Use Lambda SnapStart (Java): Takes a snapshot of the initialised environment and restores it for new invocations, cutting Java cold starts from several seconds to under 1 second.
import boto3

# Module-level init: runs once at cold start, reused on warm invocations
s3_client = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('my-table')

def handler(event, context):
    # s3_client and table are already initialised; no re-creation on warm calls
    result = table.get_item(Key={'id': event['id']})
    return result.get('Item')
Tip

Moving client initialisation outside the handler is one of the highest-impact Lambda optimisations. It can reduce effective latency by 20–200ms per warm invocation without changing any business logic, and it costs nothing extra.

Key limits and quotas

LimitValueNotes
Maximum timeout15 minutesHard limit, cannot be increased. Use AWS Batch or ECS for longer jobs.
Maximum memory10,240 MB (10 GB)CPU scales proportionally with memory. Higher memory often reduces duration enough to lower total cost.
Ephemeral storage (/tmp)512 MB – 10,240 MBConfigurable per function. Not persisted between invocations.
Deployment package (zip)50 MB compressed / 250 MB unzippedUpload larger packages via S3.
Container image size10 GBStored in Amazon ECR. Useful for large dependencies or ML models.
Concurrent executions1,000 per region (default)Account-wide across all functions. Request an increase via AWS Support. Use reserved concurrency to protect critical functions.
Synchronous payload6 MB request / 6 MB responsePass large data via S3 and include only the reference in the payload.
Asynchronous payload256 KBApplies to async invocations, SQS events, and SNS messages.
Watch out

The 1,000 concurrent execution limit applies across your entire AWS account, not just one function. A single function receiving a traffic spike can consume all available concurrency and throttle every other Lambda function in your account, including critical production services. Set reserved concurrency on important functions to protect them from this.

Pricing explained simply

Lambda pricing has two components:

  • Requests: $0.20 per 1 million requests. The first 1 million requests per month are free under the permanent free tier.
  • Duration: Billed in 1 ms increments, based on GB-seconds (memory allocated × execution time). A 1 GB function running for 1 second = 1 GB-second ≈ $0.0000167. The first 400,000 GB-seconds per month are free.

A concrete example: a 128 MB function that runs for 200ms and handles 5 million requests per month costs roughly $1.70 in duration charges plus $0.80 in request charges, under $3 total. At low to moderate volumes, Lambda is inexpensive.

At high sustained throughput, a reserved EC2 instance or Fargate service typically becomes cheaper. The break-even point depends on your function’s memory, execution time, and invocation rate. See Lambda cost optimisation for guidance on right-sizing memory, reducing cold starts, and managing concurrency costs.

Note

Provisioned concurrency costs extra. You pay for pre-warmed environments whether they receive traffic or not. Only enable it for functions where cold start latency is genuinely unacceptable.

Security and permissions basics

Every Lambda function runs with an execution role: an IAM role that Lambda assumes at runtime. This role determines what AWS services and resources your function can access. If your function reads from S3 and writes to DynamoDB, the execution role needs permissions for those specific actions on those specific resources. See IAM roles explained for the full background.

Apply least privilege: grant only the permissions the function actually needs, scoped to the specific resources it accesses. Over-permissioned functions are a common mistake. If a function is compromised, broad permissions increase the blast radius significantly.

For secrets and API keys, do not hardcode them in environment variables or embed them in function code. Use AWS Secrets Manager and retrieve them at runtime. The Lambda security model guide covers execution roles, VPC access, resource-based policies, and encryption at rest and in transit.

Monitoring and debugging Lambda

Lambda integrates with CloudWatch automatically. Every invocation is logged to a CloudWatch log group named /aws/lambda/your-function-name. This is where to look first when something breaks.

Key metrics to monitor in CloudWatch:

  • Errors: invocations that threw an uncaught exception or returned an error response.
  • Throttles: invocations rejected because the function hit its concurrency limit.
  • Duration: execution time per invocation. Watch for functions approaching their configured timeout.
  • ConcurrentExecutions: how many instances are running at once. Useful for spotting traffic spikes or approaching the account limit.
  • InitDuration: cold start initialisation time. Consistently high values point to large packages or slow init code.

For tracing latency across multiple services, enable AWS X-Ray active tracing on the function. X-Ray captures timing for downstream calls to DynamoDB, S3, and other services in a visual trace map. See the Monitoring Lambda guide for dashboard setup and alerting configuration.

When Lambda fails to start rather than throwing a runtime error, the failure mode is different. See Lambda function failed to start and debugging Lambda failures for structured troubleshooting steps.

Common mistakes

  1. Using Lambda for long-running jobs. Lambda has a hard 15-minute timeout. If your workload needs longer, use AWS Batch, ECS, or EC2. Trying to work around the limit by chaining Lambda calls is fragile and expensive.
  2. Initialising SDK clients inside the handler. Creating a boto3 client or database connection inside the handler means re-creating it on every invocation. Move these to module-level code. They initialise once at cold start and are reused on warm calls.
  3. Wrong memory allocation. Lambda CPU scales with memory. A function that runs in 5 seconds at 128 MB might finish in 1 second at 512 MB, often at lower total cost because duration drops faster than price rises. Use AWS Lambda Power Tuning to find the right size.
  4. No concurrency limits set. A misbehaving function or sudden traffic spike can consume all 1,000 default concurrent executions in your account, throttling everything else. Set reserved concurrency on critical functions to protect them.
  5. Over-permissioned execution roles. Granting * or overly broad IAM actions to a Lambda function is a common mistake. If the function only reads one S3 bucket, restrict the execution role to that bucket only.
  6. Ignoring retries and idempotency. Asynchronous Lambda invocations retry twice on failure. SQS-triggered functions retry until the message expires or hits a dead-letter queue. Make sure your handler handles duplicate invocations safely.
  7. Overusing Lambda for steady always-on services. If your Lambda API receives consistent traffic all day, every day, the per-request billing adds up. At sustained throughput, an ECS service or reserved EC2 instance is often cheaper and simpler to operate.

Frequently asked questions

What is AWS Lambda in simple terms?

AWS Lambda is a serverless compute service. You write a function, upload it to AWS, and Lambda runs it in response to events: an HTTP request, an S3 upload, a scheduled timer, or a message in a queue. You pay only when your code is running. AWS handles server provisioning, scaling, and patching.

When should I use Lambda instead of EC2?

Use Lambda for short-lived, stateless, event-driven workloads with bursty or unpredictable traffic: API backends, image processing, scheduled tasks. Use EC2 or containers when you need runtimes longer than 15 minutes, sustained high-throughput workloads, stateful applications, or full OS-level control.

What is a cold start in Lambda?

A cold start happens when Lambda creates a new execution environment for your function. AWS downloads your code, initialises the runtime, and runs your init code before calling the handler, adding anywhere from 100ms to several seconds of latency. Subsequent calls to the same environment are warm starts and have no cold start overhead.

What are Lambda's main limits?

Maximum execution timeout is 15 minutes. Maximum memory is 10 GB. Maximum deployment package is 250 MB unzipped (or 10 GB as a container image). Default concurrent executions are 1,000 per account per region. This limit can be raised via AWS Support.

Can Lambda run container images?

Yes. Lambda supports container images up to 10 GB stored in Amazon ECR. This lets you use any language or dependency set, package large ML models, and use familiar Docker tooling, while still getting Lambda's event-driven, pay-per-use model.

Last verified: 1 April 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.