← Back to list

AWS Lambda (Serverless) Complete Guide: Stop Managing Servers and Start Shipping Code

Everything you need to know about AWS Lambda — from what it is and how it works, to triggers, cold starts, pricing, real-world patterns…

Santosh Pathak in AWS Tip · 2026-06-16 05:07 · 65 claps · 18.8 min read
#aws #serverless #event-driven-architecture #devops #lamda
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 🏛️ · Architecture

AWS Lambda (Serverless) Complete Guide: Stop Managing Servers and Start Shipping Code

Everything you need to know about AWS Lambda — from what it is and how it works, to triggers, cold starts, pricing, real-world patterns, and production best practices. Written for developers who are done babysitting servers.

Imagine you wrote a function that resizes every image uploaded to your app. With a traditional server setup, you would provision an EC2 instance, configure it, keep it running 24/7, patch it, monitor it — and pay for it even at 3 AM when no one is uploading anything.

With AWS Lambda, you write the function. You tell it what should trigger it (a file upload, an API call, a database change). AWS runs it when it needs to run, scales it automatically when a thousand uploads happen simultaneously, and charges you for nothing when it is idle.

That is the promise of serverless. And Lambda is how AWS delivers it.

This guide covers everything — from the very basics to production-ready patterns — in plain language, with real code examples and authentic links to go deeper on every topic.

Table of Contents

  1. What is Serverless? And What Problem Does Lambda Solve?
  2. How AWS Lambda Actually Works
  3. The Lambda Execution Lifecycle (Init → Invoke → Shutdown)
  4. Cold Starts — The Most Talked-About Lambda Problem
  5. Writing Your First Lambda Function
  6. Lambda Triggers — What Can Invoke a Lambda?
  7. Real-World Use Cases and Patterns
  8. Lambda + S3: The Classic Pattern
  9. Lambda + API Gateway: A Serverless REST API
  10. Lambda + DynamoDB Streams
  11. Lambda + SQS: Reliable Queue Processing
  12. Lambda Layers, Versions, and Aliases
  13. Concurrency — How Lambda Scales
  14. AWS Lambda Pricing — What You Actually Pay
  15. Lambda Limits You Must Know Before Going to Production
  16. Security Best Practices
  17. Observability — Logs, Metrics, and Tracing
  18. When NOT to Use Lambda
  19. Quick Reference — Lambda Cheat Sheet

1. What is Serverless? And What Problem Does Lambda Solve?

“Serverless” does not mean there are no servers. There are absolutely servers — you just do not manage them. AWS manages the underlying infrastructure. You manage only your code.

Before Lambda, a typical backend deployment looked like this:

You → Provision an EC2 instance
    → Install runtime (Node.js, Python, Java)
    → Configure security groups
    → Deploy your application
    → Set up auto-scaling rules
    → Monitor CPU and memory
    → Apply OS patches
    → Pay $X per hour whether traffic is high or zero

AWS Lambda flips this model entirely:

You → Write a function
    → Upload it to Lambda
    → Define what triggers it
    → AWS handles everything else
    → Pay only when the function actually runs

Lambda is AWS’s Function-as-a-Service (FaaS) offering. It runs your code in response to events, scales automatically from zero to thousands of concurrent executions, and bills you in milliseconds of actual compute time.

Key mental model: With EC2, you rent a server. With Lambda, you rent execution time.

📖 Read more: AWS Lambda Official Documentation

2. How AWS Lambda Actually Works

When you create a Lambda function, you provide:

  1. Your code — packaged as a ZIP file or a container image
  2. The runtime — Node.js, Python, Java, Go, Ruby, .NET, or a custom runtime
  3. Memory allocation — from 128 MB to 10,240 MB (10 GB)
  4. Timeout — how long the function can run before being forcibly stopped (max: 15 minutes)
  5. IAM Role — what AWS services the function is allowed to access
  6. Triggers — which events should invoke the function

When a trigger fires, AWS spins up an isolated execution environment (a micro-VM), loads your code into it, and runs your handler function with the event data as input. After the function finishes, AWS freezes the environment and may reuse it for the next invocation — or shut it down if it sits idle too long.

What an Execution Environment Is

Each execution environment is a completely isolated sandbox with:

  • Your allocated memory and proportional CPU
  • 512 MB of ephemeral /tmp storage (can be configured up to 10 GB)
  • No shared state with any other execution environment
  • Your function code, dependencies, and environment variables

Multiple simultaneous invocations of the same function run in separate execution environments — they cannot see each other’s memory, files, or local variables.

📖 Read more: AWS Lambda Execution Environment — Official AWS Docs

3. The Lambda Execution Lifecycle

Every Lambda invocation goes through three phases. Understanding this is critical for writing efficient functions and understanding your bill.

┌─────────────────────────────────────────────────────────────┐
│                  LAMBDA EXECUTION LIFECYCLE                 │
│                                                             │
│  ┌──────────────┐   ┌──────────────┐   ┌────────────────┐  │
│  │  INIT Phase  │   │ INVOKE Phase │   │ SHUTDOWN Phase │  │
│  │              │→  │              │→  │                │  │
│  │ Download code│   │  Run your    │   │  Environment   │  │
│  │ Setup runtime│   │  handler()   │   │  frozen or     │  │
│  │ Load deps    │   │  function    │   │  terminated    │  │
│  │ Run init code│   │              │   │                │  │
│  └──────────────┘   └──────────────┘   └────────────────┘  │
│       COLD START                              WARM START     │
│   (first invocation            (subsequent invocations skip │
│    or new environment)          the INIT phase entirely)    │
└─────────────────────────────────────────────────────────────┘

INIT Phase (Cold Start)

This only happens when AWS needs to create a new execution environment. Lambda:

  1. Downloads your function code from S3 (or ECR for container images)
  2. Configures the execution environment with your memory settings
  3. Initializes the runtime (Node.js, Python interpreter, JVM, etc.)
  4. Runs any code outside your handler function — SDK initializations, DB connections, global variables

The INIT phase is capped at 10 seconds. If your initialization code takes longer, Lambda retries it.

INVOKE Phase

This is where your actual handler function runs. The event data (what triggered the function) is passed in as an argument. Your function processes it and returns a response (or writes to another service).

SHUTDOWN Phase

If the function sits idle too long (typically 5–15 minutes, though AWS does not publish an exact value), AWS terminates the environment. The SHUTDOWN phase lasts at most 2 seconds and allows any cleanup code (registered extensions) to run.

Important billing update (August 2025): AWS now charges for the INIT phase at the same rate as the INVOKE phase. Previously, the INIT phase was free for ZIP-packaged functions using managed runtimes. This change primarily impacts Java and .NET functions where initialization can take 500ms–2 seconds. For Python and Node.js, the impact is minimal.

📖 Read more: AWS Lambda Standardizes Billing for INIT Phase — AWS Compute Blog

4. Cold Starts — The Most Talked-About Lambda Problem

A cold start is when your Lambda function is invoked but there is no warm execution environment available. AWS must go through the full INIT phase before running your code. This adds latency — anywhere from 100ms to over 2 seconds depending on the runtime and your initialization code.

Cold Start Times by Runtime (approximate)

Runtime Typical Cold Start Python 3.12 100–300ms Node.js 20 100–300ms Go 50–150ms Java 21 (without SnapStart) 500ms–3s .NET 8 300ms–1.5s Java 21 (with SnapStart) 100–200ms

What Causes Cold Starts

  • Your function has not been invoked recently and the environment was shut down
  • Traffic spikes that require new environments to be created
  • Deployments — new code means new environments

How to Fix Them

Option 1 — Provisioned Concurrency Keeps a specified number of execution environments pre-initialized and ready to respond with zero cold start latency. You pay for the reserved capacity even when idle. Best for payment flows, authentication, and any user-facing API where latency directly affects experience.

Option 2 — Lambda SnapStart (Java and now Python) SnapStart takes a snapshot of the initialized execution environment and restores it on invocation instead of re-initializing from scratch. Originally Java-only, it expanded to Python in November 2024 and delivers up to 4.3x improvement in cold start performance.

Option 3 — Optimize Your Initialization Code

  • Move SDK client creation and DB connections outside the handler (so they are reused across warm invocations)
  • Lazy-load modules you do not always need
  • Minimize the number of dependencies — a leaner deployment package initializes faster
  • Use Lambda Layers for shared dependencies so they are pre-loaded

Option 4 — Choose Lightweight Runtimes If cold starts are critical and you have flexibility, Python and Node.js warm up significantly faster than Java or .NET. Go is the fastest of all.

# BAD: DB connection created inside handler = runs on every invocation
def handler(event, context):
    db = create_db_connection()  # Slow! Runs every time.
    return db.query("SELECT ...")
# GOOD: DB connection created outside handler = reused across warm invocations
db = create_db_connection()  # Runs only during INIT phase
def handler(event, context):
    return db.query("SELECT ...")  # Fast! Connection already ready.

Reality check: AWS has stated that fewer than 1% of Lambda invocations experience a cold start. For most workloads, cold starts are not the catastrophe they are often portrayed as. Optimize when you have measured evidence they are a problem — not preemptively.

📖 Read more: AWS Lambda Cold Start: 7 Proven Fixes — AgileSoftLabs 📖 Read more: AWS Lambda Cold Start Optimization: What Actually Works — Zircon.tech

5. Writing Your First Lambda Function

A Lambda function is just a file with a handler function. Here is the simplest possible example in Python and Node.js:

Python

import json
def lambda_handler(event, context):
    """
    event   → The data that triggered this function (a dict)
    context → Runtime info (function name, memory, remaining time, etc.)
    """
    name = event.get("name", "World")

    return {
        "statusCode": 200,
        "body": json.dumps({"message": f"Hello, {name}!"})
    }

Node.js

export const handler = async (event, context) => {
    const name = event.name ?? "World";

    return {
        statusCode: 200,
        body: JSON.stringify({ message: `Hello, ${name}!` })
    };
};

The event Object

The shape of event depends on what triggered your function. An S3 trigger sends an event that looks like:

{
  "Records": [
    {
      "s3": {
        "bucket": { "name": "my-uploads-bucket" },
        "object": { "key": "photos/user123/avatar.jpg", "size": 104857 }
      }
    }
  ]
}

An API Gateway trigger sends:

{
  "httpMethod": "POST",
  "path": "/users",
  "headers": { "Content-Type": "application/json" },
  "body": "{\"name\": \"Rahul\", \"email\": \"rahul@example.com\"}"
}

Your function reads the event, does its work, and returns a response appropriate for the trigger type.

📖 Read more: AWS Lambda Function Handler — Official Docs

6. Lambda Triggers — What Can Invoke a Lambda?

This is where Lambda’s power becomes clear. Lambda integrates natively with almost every AWS service. Here is the full picture:

┌─────────────────────────────────────────────────────────────┐
│                  WHAT CAN TRIGGER LAMBDA                    │
│                                                             │
│  HTTP / API             Storage               Messaging     │
│  ─────────────          ───────────           ──────────    │
│  API Gateway            S3 (file upload)      SQS           │
│  Application LB         DynamoDB Streams      SNS           │
│  Function URL           RDS Proxy             EventBridge   │
│                         EFS                  Kinesis        │
│                                                             │
│  Scheduling             Database              Auth           │
│  ──────────             ────────              ────           │
│  EventBridge            Aurora                Cognito        │
│  (cron jobs)            DynamoDB Streams      (user events) │
│                                                             │
│  Code / DevOps          AI/ML                 IoT            │
│  ─────────────          ─────                 ───            │
│  CodePipeline           Lex                   IoT Rules      │
│  CodeCommit             Rekognition triggers  Greengrass     │
│                         SageMaker                           │
│                                                             │
│  Other                                                      │
│  ─────                                                      │
│  CloudWatch Logs        Step Functions        Direct Invoke  │
│  CloudFront (Lambda@Edge)                    (SDK/CLI)      │
└─────────────────────────────────────────────────────────────┘

Each trigger type passes a different event structure to your handler. AWS provides event schema documentation for all supported sources.

📖 Read more: Lambda Event Source Mappings — AWS Docs

7. Real-World Use Cases and Patterns

Lambda is not a silver bullet for everything, but for specific workloads it is the most efficient tool available.

Use Case Why Lambda Fits Image/video processing on upload Event-driven, short bursts of work, scales to thousands simultaneously REST API backend Scales from 0 to millions with no capacity planning Scheduled jobs (cron) No server to keep running just to run a job once an hour ETL and data pipelines Process S3 files or DynamoDB changes as they arrive Webhooks Receive and process third-party events (Stripe, GitHub, Slack) Real-time notifications React to database changes and send push notifications/emails Authentication flows Run authorizer functions before API requests reach your service Chatbots Stateless request-response fits Lambda’s model perfectly

8. Lambda + S3: The Classic Pattern

The most common Lambda pattern. An image is uploaded to S3 → Lambda is triggered → Lambda processes the image (resize, compress, extract metadata, scan for viruses) → Saves the result back to S3 or DynamoDB.

User uploads photo
       ↓
   S3 Bucket (source)
       ↓  [S3 Event Notification]
  Lambda Function
       ↓  (resize to 300×300, convert to WebP)
   S3 Bucket (processed)
       ↓
   DynamoDB (stores metadata: filename, size, URL)

The Code

import boto3
from PIL import Image
import io
s3 = boto3.client("s3")
OUTPUT_BUCKET = "my-processed-images"
def lambda_handler(event, context):
    # Extract bucket name and file key from the S3 event
    record = event["Records"][0]
    source_bucket = record["s3"]["bucket"]["name"]
    object_key = record["s3"]["object"]["key"]

    # Download the original image from S3
    response = s3.get_object(Bucket=source_bucket, Key=object_key)
    image_data = response["Body"].read()

    # Resize the image
    image = Image.open(io.BytesIO(image_data))
    image.thumbnail((300, 300))

    # Save the resized image back to S3
    output_buffer = io.BytesIO()
    image.save(output_buffer, format="WEBP")
    output_buffer.seek(0)

    output_key = f"thumbnails/{object_key}"
    s3.put_object(
        Bucket=OUTPUT_BUCKET,
        Key=output_key,
        Body=output_buffer,
        ContentType="image/webp"
    )

    return {"processed": output_key}

📖 Read more: Using AWS Lambda with Amazon S3 — Official Docs

9. Lambda + API Gateway: A Serverless REST API

API Gateway sits in front of Lambda, receiving HTTP requests and routing them to your Lambda functions. The combination gives you a fully serverless REST API with zero server management and automatic scaling.

Client (Browser / Mobile App)
         ↓  HTTPS
    API Gateway
         ↓  Invokes Lambda per route
  ┌──────┴──────┬──────────────────┐
GET /users   POST /users    DELETE /users/{id}
     ↓              ↓                  ↓
 Lambda fn      Lambda fn          Lambda fn
     ↓              ↓                  ↓
                DynamoDB

Lambda Function for a REST Endpoint

import json
import boto3
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Users")
def lambda_handler(event, context):
    method = event["httpMethod"]
    path = event["path"]

    if method == "GET" and path == "/users":
        response = table.scan()
        return {
            "statusCode": 200,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps(response["Items"])
        }

    if method == "POST" and path == "/users":
        body = json.loads(event["body"])
        table.put_item(Item={
            "userId": body["userId"],
            "name": body["name"],
            "email": body["email"]
        })
        return {
            "statusCode": 201,
            "body": json.dumps({"message": "User created"})
        }

    return {"statusCode": 404, "body": "Not found"}

📖 Read more: Building a Serverless API with Lambda and API Gateway — AWS Tutorial

10. Lambda + DynamoDB Streams

DynamoDB Streams captures every INSERT, MODIFY, and DELETE on a DynamoDB table and delivers them to your Lambda function in real time. This is the foundation of event-driven architectures with DynamoDB.

When is this useful?

  • Send a welcome email when a new user record is created
  • Update a search index when a product record changes
  • Sync data to a reporting database
  • Trigger notifications when an order status changes
def lambda_handler(event, context):
    for record in event["Records"]:
        event_type = record["eventName"]  # INSERT, MODIFY, or REMOVE

        if event_type == "INSERT":
            new_user = record["dynamodb"]["NewImage"]
            user_email = new_user["email"]["S"]
            user_name = new_user["name"]["S"]

            send_welcome_email(user_email, user_name)
            print(f"Welcome email sent to {user_email}")

        elif event_type == "MODIFY":
            old_status = record["dynamodb"]["OldImage"]["status"]["S"]
            new_status = record["dynamodb"]["NewImage"]["status"]["S"]

            if old_status != new_status:
                handle_status_change(record, old_status, new_status)

📖 Read more: Using AWS Lambda with DynamoDB Streams — AWS Docs

11. Lambda + SQS: Reliable Queue Processing

SQS (Simple Queue Service) combined with Lambda is the go-to pattern for reliable, at-least-once background job processing. When messages arrive in an SQS queue, Lambda polls the queue and invokes your function with batches of messages.

API receives order
      ↓
  Puts message in SQS Queue
      ↓  (Lambda polls for new messages)
  Lambda Function
      ↓
  Process order (charge card, reserve inventory, send confirmation)
      ↓
  Message deleted from queue if processing succeeds
  Message returns to queue if Lambda throws an error → automatic retry
import json
def lambda_handler(event, context):
    for record in event["Records"]:
        message_body = json.loads(record["body"])
        order_id = message_body["orderId"]

        try:
            process_order(order_id)
            print(f"Order {order_id} processed successfully")
        except Exception as e:
            print(f"Failed to process order {order_id}: {e}")
            raise  # Re-raise so Lambda knows this message failed
            # Failed message goes back to the queue for retry
            # After max retries, it moves to the Dead Letter Queue (DLQ)

Why SQS + Lambda?

  • If processing fails, the message is not deleted — it retries automatically
  • Set a Dead Letter Queue (DLQ) to capture messages that fail after all retries
  • Increase the batch size (up to 10,000 messages) to process more messages per Lambda invocation — reduces costs significantly
  • Lambda scales the number of concurrent executions based on queue depth

📖 Read more: Using AWS Lambda with Amazon SQS — AWS Docs

12. Lambda Layers, Versions, and Aliases

Lambda Layers

A Layer is a ZIP archive that contains shared code, libraries, or binaries that multiple functions can reference. Instead of packaging numpy, pandas, or your company's shared utilities into every function's deployment package, you package them once as a Layer and attach it to functions.

Function A      Function B      Function C
    ↓               ↓               ↓
    └───────────────┴───────────────┘
                    ↓
             Lambda Layer
        (pandas, numpy, shared_utils)

Benefits:

  • Smaller deployment packages — functions deploy faster
  • Shared updates — update the Layer once, all functions pick it up on next deploy
  • Reuse across accounts — Layers can be published publicly or shared with specific AWS accounts

AWS also provides AWS-managed Layers (like the AWS SDK, which is already included in managed runtimes).

Versions

Every time you deploy new code to a Lambda function, Lambda creates a new version — an immutable snapshot of your function code and configuration. Versions are numbered ($LATEST is always the unpublished latest).

Aliases

An Alias is a named pointer to a specific version. Instead of hardcoding a version number in your triggers and API configurations, you point them at an alias (prod, staging). When you deploy a new version, you update the alias pointer — no trigger reconfiguration needed.

Aliases also support traffic shifting (canary deployments) — you can send 10% of traffic to the new version and 90% to the old version while you validate.

API Gateway → Lambda Alias "prod"
                   ↓
          90% → Version 7 (current stable)
          10% → Version 8 (canary)

📖 Read more: Lambda Layers — AWS Docs 📖 Read more: Lambda Versions and Aliases — AWS Docs

13. Concurrency — How Lambda Scales

Lambda scales by creating new execution environments in parallel. Each concurrent invocation runs in its own execution environment.

Types of Concurrency

Unreserved Concurrency The default pool — all functions in your account share from the account limit (1,000 concurrent executions per region by default). If one function spikes to 900 concurrent executions, it can starve other functions.

Reserved Concurrency Set a maximum concurrency limit for a specific function. This does two things:

  1. Guarantees that concurrency up to this limit is always available to this function
  2. Caps the function so it cannot consume the entire account pool
# Useful pattern: Set reserved concurrency on non-critical functions
# to prevent them from monopolizing the account pool
# E.g., set "report-generator" to max 50 concurrency

Provisioned Concurrency Keeps a specified number of execution environments pre-initialized and always warm. Eliminates cold starts entirely for that capacity. You pay for these even when they are idle — use it only for user-facing functions where latency directly matters.

Concurrency Limits

The default account limit is 1,000 concurrent executions per region. This is a soft limit — you can request an increase from AWS Support. However, this shared pool applies to all functions across all services in that region. Plan accordingly and use Reserved Concurrency to protect critical functions.

Real-world warning: A SaaS platform launched on Product Hunt and saw 14,000 concurrent users instead of the expected 2,000. Their Lambda functions hit the 1,000 concurrent execution limit, causing 96% of requests to be throttled. Always request a limit increase before a major traffic event.

📖 Read more: Lambda Concurrency — AWS Docs

14. AWS Lambda Pricing — What You Actually Pay

Lambda pricing has two components: requests and compute duration.

Free Tier (Always Free — does not expire)

Component Free Tier Requests 1,000,000 per month Compute duration 400,000 GB-seconds per month

For many small projects and internal tools, Lambda runs entirely within the free tier indefinitely.

Paid Pricing (us-east-1, x86)

Component Price Requests $0.20 per 1 million requests Compute duration $0.0000166667 per GB-second Arm64 (Graviton2) duration ~20% cheaper than x86

A Practical Example

A serverless API endpoint with:

  • 128 MB memory
  • 1 million requests per month
  • 200ms average execution time per request

Compute: 1,000,000 × 0.200s × (128/1024 GB) = 25,000 GB-seconds After free tier: 25,000–400,000 = 0 GB-seconds billed (within free tier) Requests cost: (1,000,000–1,000,000) = $0 (within free tier)

Total: $0/month

For a heavier workload — 10 million requests at 512 MB memory and 500ms execution:

  • Compute: 10M × 0.5s × 0.5 GB = 2,500,000 GB-seconds
  • After free tier: 2,100,000 GB-seconds × $0.0000166667 = ~$35.00
  • Requests: (10M — 1M) × $0.20/1M = $1.80
  • Total: ~$36.80/month

Hidden Costs to Watch

Trigger service costs: API Gateway ($3.50 per million requests), EventBridge ($1 per million events), SQS ($0.40 per million requests) — these are separate from Lambda’s own cost.

VPC NAT Gateway: If your Lambda function is inside a VPC and needs internet access or access to AWS services, traffic routes through a NAT Gateway at ~$0.045/hr plus data charges. Use VPC Endpoints for S3 and DynamoDB (S3 and DynamoDB Gateway Endpoints are completely free) and Interface Endpoints for other services to avoid NAT Gateway costs.

CloudWatch Logs: Lambda writes logs to CloudWatch automatically. The default retention is forever, which accumulates storage charges. Set a log retention policy (7, 14, or 30 days is usually sufficient).

📖 Read more: AWS Lambda Pricing — Official Page 📖 Read more: AWS Lambda Cost Breakdown 2026 — Wiz Academy

15. Lambda Limits You Must Know Before Going to Production

These are the hard limits that define whether Lambda is the right tool for your use case.

Limit Value Notes Maximum execution timeout 15 minutes For longer tasks, use Step Functions or ECS Fargate Maximum memory 10,240 MB (10 GB) CPU scales proportionally with memory Deployment package size (ZIP) 50 MB (compressed) Use Lambda Layers or container images for larger dependencies Container image size 10 GB For ML models and large binaries /tmp ephemeral storage 512 MB – 10 GB Not shared between invocations Synchronous response payload 6 MB Use S3 for large responses; use response streaming (up to 200 MB, added Oct 2025) Default concurrent executions 1,000 per region Soft limit — request an increase before events Environment variables 4 KB total Store large configs in SSM Parameter Store or Secrets Manager Layers per function 5 maximum

📖 Read more: AWS Lambda Quotas — Official Docs

16. Security Best Practices

Lambda security follows the principle of least privilege — give each function only the permissions it needs to do its specific job.

IAM Role (Execution Role)

Every Lambda function has an IAM Role that defines what AWS resources it can access. Create a separate, narrow role per function.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::my-source-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-output-bucket/*"
    }
  ]
}

This function can read from one bucket and write to another — nothing else.

Never Store Secrets in Environment Variables in Plain Text

Instead, store secrets in AWS Secrets Manager or SSM Parameter Store and retrieve them at runtime. For frequently accessed secrets, cache the value outside the handler function so you fetch it only once per execution environment.

import boto3
import json
# Fetch secret once per cold start — cached in global variable
ssm = boto3.client("ssm")
_db_password = None
def get_db_password():
    global _db_password
    if _db_password is None:
        response = ssm.get_parameter(
            Name="/myapp/prod/db-password",
            WithDecryption=True
        )
        _db_password = response["Parameter"]["Value"]
    return _db_password
def lambda_handler(event, context):
    password = get_db_password()  # Cached after first invocation
    # ... use password

VPC for Database Access

If your Lambda function accesses a private RDS database, place the function inside the same VPC and subnet as the database. Use Security Groups to allow traffic from Lambda’s security group to the database’s security group on the correct port.

📖 Read more: Lambda Security Best Practices — AWS Docs

17. Observability — Logs, Metrics, and Tracing

CloudWatch Logs

Every print() or console.log() statement in your Lambda function automatically goes to CloudWatch Logs. No configuration needed — it just works.

Each function gets a Log Group named /aws/lambda/your-function-name. Each execution environment writes to its own Log Stream.

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
    logger.info(f"Processing order: {event.get('orderId')}")

    try:
        result = process_order(event)
        logger.info(f"Order processed successfully: {result}")
        return result
    except Exception as e:
        logger.error(f"Order processing failed: {str(e)}", exc_info=True)
        raise

CloudWatch Metrics

Lambda automatically publishes metrics to CloudWatch:

  • Invocations — total function calls
  • Duration — execution time (min, max, average, P50, P99)
  • Errors — failed invocations
  • Throttles — invocations rejected due to concurrency limit
  • ConcurrentExecutions — real-time concurrency usage
  • InitDuration — time spent in the INIT phase (cold starts)

AWS X-Ray (Distributed Tracing)

Enable X-Ray tracing on your Lambda function to get a visual trace of each invocation — showing which downstream AWS services were called, how long each took, and where errors occurred. Essential for debugging performance issues in functions that call multiple services.

📖 Read more: Monitoring Lambda with CloudWatch — AWS Docs

18. When NOT to Use Lambda

Lambda is not the right tool for every problem. Here are the situations where you should reach for something else:

Situation Why Lambda is wrong Better alternative Tasks longer than 15 minutes Lambda has a hard timeout AWS Fargate, EC2, Step Functions Steady high-volume traffic (50M+ req/month) EC2 or Fargate is 70–90% cheaper at constant load EC2 with Auto Scaling, ECS Fargate Sub-10ms P99 latency requirements Lambda’s invocation overhead and cold start risk make this nearly impossible EC2 with pre-warmed servers Heavy compute — video transcoding, ML training Lambda’s max 10 GB memory and 15 min timeout are insufficient EC2 with GPU, AWS Batch Stateful long-running connections — WebSockets Lambda is stateless and short-lived API Gateway WebSocket + DynamoDB, or EC2/ECS Running a traditional web framework that manages its own server lifecycle Works but adds unnecessary complexity ECS Fargate, Elastic Beanstalk

The honest take: Lambda is extraordinary for event-driven, bursty, short-duration workloads. For steady high-volume workloads or tasks requiring persistent compute, traditional server-based solutions are often more cost-effective and simpler to operate.

📖 Read more: AWS Lambda Limitations: Complete Guide — Medium

19. Quick Reference — Lambda Cheat Sheet

SUPPORTED RUNTIMES
──────────────────
Python 3.12, 3.13
Node.js 20, 22
Java 17, 21
.NET 8
Go 1.x
Ruby 3.3
Custom Runtime (any language via Runtime API)
KEY LIMITS
──────────
Max timeout:         15 minutes
Max memory:          10,240 MB
Default concurrency: 1,000 per region (soft limit)
Max payload (sync):  6 MB (or 200 MB with response streaming)
Deployment ZIP:      50 MB compressed / 250 MB uncompressed
Container image:     10 GB
Layers per function: 5
PRICING (us-east-1, x86)
─────────────────────────
Free tier:    1M requests + 400,000 GB-seconds/month (always free)
Requests:     $0.20 per 1M after free tier
Duration:     $0.0000166667 per GB-second
Arm64:        ~20% cheaper on duration
COLD START QUICK WINS
─────────────────────
✓ Initialize SDK clients and DB connections outside handler
✓ Minimize dependencies in your deployment package
✓ Use SnapStart for Java/Python (4.3x improvement)
✓ Use Graviton2 (arm64) — 13–24% faster init
✓ Use Provisioned Concurrency only when measured latency matters
TRIGGER CHEAT SHEET
───────────────────
S3              → File processing (images, CSV, PDFs)
API Gateway     → REST API / GraphQL / webhooks
DynamoDB Stream → React to database changes in real time
SQS             → Reliable async background job processing
EventBridge     → Scheduled jobs (cron), cross-service events
SNS             → Fan-out notifications
Kinesis         → Real-time streaming data processing
Cognito         → Pre/post authentication hooks
SECURITY CHECKLIST
──────────────────
□ Separate IAM role per function (least privilege)
□ No secrets in environment variables — use Secrets Manager or SSM
□ Enable CloudWatch logging with a retention policy
□ Enable X-Ray tracing for production functions
□ Put DB-connected functions inside a VPC
□ Use VPC Endpoints (not NAT Gateway) for AWS service access
□ Set Reserved Concurrency on critical functions

What to Learn Next

Once you are comfortable with Lambda fundamentals, here is the natural progression:

  • AWS Step Functions — orchestrate multi-step workflows involving multiple Lambda functions, with built-in retry, error handling, and branching logic
  • AWS SAM (Serverless Application Model) — infrastructure-as-code framework purpose-built for Lambda, API Gateway, and DynamoDB
  • AWS CDK — define your entire serverless infrastructure in TypeScript, Python, or Java
  • Lambda Power Tuning — an open-source tool that tests your function at different memory sizes and finds the optimal cost/performance balance
  • EventBridge — the event bus that ties all your Lambda functions together in complex event-driven architectures

📖 Start here: AWS Serverless Learning Path — AWS Skill Builder 📖 Hands-on labs: AWS Lambda Workshop — workshops.aws

Final Thoughts

Lambda changed the way teams think about backend infrastructure. Not every team should go all-in on serverless — and this guide has tried to be honest about where Lambda fits and where it does not. But for event-driven workloads, short-lived processing tasks, and APIs with unpredictable or bursty traffic, Lambda is genuinely transformative.

The operational burden it eliminates — no server provisioning, no OS patching, no capacity planning, no paying for idle compute — frees engineering teams to focus on what actually matters: the business logic inside the function.

Start small. Wire up an S3 trigger or a simple API Gateway route. Once you feel how little infrastructure stands between your code and production, it is hard to go back.

Was this guide useful? Share it with your team or anyone getting started with AWS.


메타데이터
post_id
dc4c3f5ee12d
slug
aws-lambda-serverless-complete-guide-stop-managing-servers-and-start-shipping-code-dc4c3f5ee12d
url
https://awstip.com/aws-lambda-serverless-complete-guide-stop-managing-servers-and-start-shipping-code-dc4c3f5ee12d
canonical_url
https://awstip.com/aws-lambda-serverless-complete-guide-stop-managing-servers-and-start-shipping-code-dc4c3f5ee12d
author_url
https://medium.com/@pathaksantosh987
status
ok
fetched_at
2026-06-22 07:15:07