AWS Lambda — Serverless Functions Complete Guide
Advertisement
Introduction
Why This Matters
AWS Lambda executes code in response to events without provisioning or managing servers. You pay only for compute time consumed — billed in 1ms increments. Lambda integrates natively with over 200 AWS services, making it the foundation for event-driven architectures: API backends, image processing pipelines, scheduled jobs, stream processors, and more. Understanding Lambda limits, cold starts, and deployment patterns is essential for building cost-effective serverless applications.
Creating Lambda Functions
# handler.py — Python Lambda handler
import json
import boto3
import os
def lambda_handler(event, context):
"""
event: trigger-specific data (API Gateway request, S3 event, etc.)
context: Lambda runtime info (function name, timeout, request ID)
"""
print(f"Event: {json.dumps(event)}")
print(f"Remaining time: {context.get_remaining_time_in_millis()}ms")
# Access environment variables
table_name = os.environ['DYNAMODB_TABLE']
# Call AWS service
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(table_name)
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps({'message': 'Success'})
}# Package and deploy
zip -r function.zip handler.py
aws lambda create-function \
--function-name my-api \
--runtime python3.12 \
--role arn:aws:iam::123456789012:role/lambda-execution-role \
--handler handler.lambda_handler \
--zip-file fileb://function.zip \
--timeout 30 \
--memory-size 256 \
--environment Variables='{DYNAMODB_TABLE=my-table}'
# Update function code
aws lambda update-function-code \
--function-name my-api \
--zip-file fileb://function.zip
# Invoke manually for testing
aws lambda invoke \
--function-name my-api \
--payload '{"key": "value"}' \
--cli-binary-format raw-in-base64-out \
response.json
cat response.jsonNode.js Lambda
// handler.js
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const client = new DynamoDBClient({ region: process.env.AWS_REGION });
exports.handler = async (event, context) => {
const { pathParameters } = event;
const userId = pathParameters?.id;
try {
const response = await client.send(new GetItemCommand({
TableName: process.env.DYNAMODB_TABLE,
Key: { userId: { S: userId } }
}));
if (!response.Item) {
return { statusCode: 404, body: JSON.stringify({ error: 'Not found' }) };
}
return {
statusCode: 200,
body: JSON.stringify(response.Item)
};
} catch (error) {
console.error('Error:', error);
return { statusCode: 500, body: JSON.stringify({ error: 'Internal error' }) };
}
};Triggers and Event Sources
# API Gateway trigger — HTTP endpoint
aws apigatewayv2 create-api \
--name my-api \
--protocol-type HTTP
aws lambda add-permission \
--function-name my-api \
--statement-id api-gateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com
# S3 trigger — on file upload
aws s3api put-bucket-notification-configuration \
--bucket my-bucket \
--notification-configuration '{
"LambdaFunctionConfigurations": [{
"LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:process-upload",
"Events": ["s3:ObjectCreated:*"],
"Filter": {"Key": {"FilterRules": [{"Name": "suffix", "Value": ".jpg"}]}}
}]
}'
# SQS trigger — process queue messages
aws lambda create-event-source-mapping \
--function-name process-orders \
--event-source-arn arn:aws:sqs:us-east-1:123456789012:orders-queue \
--batch-size 10 \
--maximum-batching-window-in-seconds 5
# EventBridge (CloudWatch Events) — scheduled job
aws events put-rule \
--name daily-cleanup \
--schedule-expression "cron(0 2 * * ? *)"Layers and Dependencies
# Create a Lambda Layer for shared dependencies
mkdir -p layer/python
pip install requests boto3 -t layer/python/
cd layer && zip -r ../my-layer.zip . && cd ..
aws lambda publish-layer-version \
--layer-name my-dependencies \
--zip-file fileb://my-layer.zip \
--compatible-runtimes python3.11 python3.12
# Attach layer to function
aws lambda update-function-configuration \
--function-name my-api \
--layers arn:aws:lambda:us-east-1:123456789012:layer:my-dependencies:1Concurrency and Cold Starts
# Set reserved concurrency (limits max concurrent executions)
aws lambda put-function-concurrency \
--function-name my-api \
--reserved-concurrent-executions 100
# Provisioned concurrency — eliminates cold starts
aws lambda put-provisioned-concurrency-config \
--function-name my-api \
--qualifier production \
--provisioned-concurrent-executions 10
# Check concurrency utilization
aws cloudwatch get-metric-statistics \
--namespace AWS/Lambda \
--metric-name ConcurrentExecutions \
--dimensions Name=FunctionName,Value=my-api \
--statistics Maximum \
--period 60 \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-01T01:00:00ZCommon Mistakes
- Setting memory too low — Lambda CPU scales proportionally with memory; 512MB often runs faster and cheaper than 128MB
- Not handling errors and retries properly — async Lambda invocations retry twice by default; write idempotent handlers
- Putting Lambda in a VPC without understanding the latency and cold start implications
- Bundling large dependencies in deployment packages — use Layers for shared libraries or container images for large runtimes
- Not setting Dead Letter Queues (DLQ) for async invocations — failed events disappear silently without DLQ
Best Practices
- Keep handlers lean — initialize AWS SDK clients outside the handler function to reuse across warm invocations
- Use environment variables for configuration; use AWS Secrets Manager for sensitive values
- Set function timeouts conservatively — default 3 seconds is too short for most real workloads
- Enable Lambda Insights (CloudWatch) for detailed performance metrics and traces
- Use Lambda Power Tuning (open source) to find the optimal memory setting for cost and performance
- Deploy with infrastructure as code (Terraform, SAM, CDK) — never manual console deployments
Key Takeaways
- Lambda runs code in response to events — you pay only for compute time in 1ms increments with no idle costs
- Cold starts occur when Lambda initializes a new execution environment — provisioned concurrency eliminates cold starts at added cost
- Memory setting directly controls CPU allocation — higher memory often reduces duration enough to lower total cost
- Layers allow sharing dependencies across multiple Lambda functions without duplicating deployment packages
- Reserved concurrency limits maximum concurrent executions — protects downstream services from overload
- Lambda integrates natively with API Gateway, S3, SQS, SNS, DynamoDB Streams, EventBridge, and Kinesis
- Container image support allows packaging up to 10GB — suitable for ML inference and large runtimes
- Initialize external connections (database, SDK clients) outside the handler to reuse across warm Lambda invocations
Advertisement
Related reading
AWS for Developers 2026 — EC2, S3, Lambda, RDS, and CloudFront Guide6 min readServerless Computing Guide 2026 — AWS Lambda, Cloudflare Workers, and Edge Functions7 min readTerraform Infrastructure Guide 2026 — Infrastructure as Code for AWS, GCP, and Azure6 min readKubernetes Guide 2026 — Deploy, Scale, and Manage Containers in Production5 min readVercel Deployment Guide 2026 — Next.js, Edge Functions, and Production Optimization6 min readAWS Cloud Cost Optimization 2026 — Cut Your Bill by 60% Without Killing Performance7 min read