Skip to content
AWS core 4 min read

Lambda & Serverless

AWS Lambda runs your code in response to events without you provisioning or managing any servers. You upload a function, AWS executes it on demand, scales it automatically from zero to thousands of concurrent invocations, and bills you only for the compute time you actually use. This is serverless: no instances to patch, no capacity to plan, no idle cost.

The serverless concept

“Serverless” doesn’t mean there are no servers - it means you never think about them. AWS owns provisioning, scaling, patching, and availability. You own only your function code and its configuration. The unit of deployment shrinks from a server to a single function, and the operational surface area collapses.

Serverless is a spectrum, not just Lambda. S3, DynamoDB, SQS, API Gateway, and EventBridge are all serverless building blocks that compose into entire applications with no servers to manage.

The event-driven model

Lambda functions are invoked by events. A function sits idle (and free) until something triggers it: an HTTP request, a file landing in S3, a message on a queue, a scheduled timer, a database change. This event-driven design makes Lambda a natural glue layer between AWS services.

Invocation comes in three flavors:

  • Synchronous — caller waits for the response (e.g. API Gateway, ALB).
  • Asynchronous — event is queued and the caller returns immediately; Lambda retries on failure (e.g. S3, SNS).
  • Stream/poll-based — Lambda polls a source and invokes in batches (e.g. Kinesis, SQS, DynamoDB Streams).

Handlers

The handler is the function AWS calls per event. It receives an event (the trigger payload) and a context (runtime metadata), and returns a response.

// index.mjs - Node.js 20 handler
export const handler = async (event, context) => {
  console.log("Request ID:", context.awsRequestId);

  const name = event.queryStringParameters?.name ?? "world";

  return {
    statusCode: 200,
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ message: `Hello, ${name}!` }),
  };
};

Deploy and invoke from the CLI:

aws lambda invoke \
  --function-name hello-devcraftly \
  --payload '{"queryStringParameters":{"name":"DevCraftly"}}' \
  --cli-binary-format raw-in-base64-out \
  response.json

Output:

{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

Triggers

Triggers connect event sources to your function. Common ones include:

TriggerUse case
API Gateway / Function URLHTTP/REST APIs and webhooks
S3Process objects on upload (thumbnails, ETL)
EventBridge / CloudWatch EventsScheduled jobs (cron) and event routing
SQS / SNSDecoupled async processing, fan-out
DynamoDB Streams / KinesisReact to data changes in real time

Cold starts

When Lambda needs a fresh execution environment - first invocation, after scaling out, or after idle reclaim - it must initialize the runtime and your code before handling the request. This added latency is a cold start.

  • Lightweight runtimes (Node.js, Python) start fastest; heavy JVM/.NET cold-start slower.
  • Keep deployment packages small and move heavy init outside the handler (it runs once per environment, then is reused on “warm” invocations).
  • Use Provisioned Concurrency to keep environments pre-warmed for latency-sensitive endpoints (at extra cost).

Cold starts mostly hurt low-traffic or spiky synchronous APIs. High-traffic functions stay warm naturally. Don’t pay for Provisioned Concurrency unless you’ve measured a real latency problem.

Pricing

Lambda billing has two components:

  1. Requests — per invocation (with a generous always-free tier of 1M/month).
  2. Duration — GB-seconds: memory allocated x execution time, billed per millisecond.

Memory and CPU scale together - more memory means proportionally more CPU, so a function can sometimes finish faster and cheaper at a higher memory setting. Profile to find the sweet spot.

When to use Lambda vs EC2/containers

ChooseWhen
LambdaEvent-driven, spiky, short tasks (<15 min); want zero ops and scale-to-zero
Containers (ECS/Fargate)Long-running services, custom runtimes, steady traffic, large images
EC2Full OS control, specialized hardware (GPU), or sustained high utilization

Lambda caps at a 15-minute timeout and limited ephemeral storage. For long-running, stateful, or constantly busy workloads, a container or EC2 is usually simpler and cheaper than fighting these limits.

Best Practices

  • Keep functions small and single-purpose; compose them with EventBridge and queues.
  • Grant each function a least-privilege execution role; never share one broad role.
  • Initialize SDK clients and DB connections outside the handler to reuse them across warm invocations.
  • Set sensible timeouts, memory, and concurrency limits; add a dead-letter queue for async failures.
  • Use structured logging to CloudWatch and tracing with X-Ray for observability.
  • Deploy with Infrastructure as Code (SAM, CDK, or Serverless Framework) for repeatable, versioned releases.
Last updated June 1, 2026
Was this helpful?