What is AWS Lambda?
AWS Lambda is a serverless compute service that lets you run code without provisioning or managing servers. You upload your code, define the execution environment, and Lambda executes it on demand — scaling automatically from a few requests per day to thousands per second. You pay only for the compute time consumed, measured in milliseconds.
At its core, Lambda follows an event-driven model. Your function sits idle until triggered by an event source — an HTTP request via API Gateway, a file upload to S3, a new message in an SQS queue, a scheduled CloudWatch event, or a direct invocation via the SDK. When triggered, Lambda initializes an execution environment, runs your code, and returns the result.
Key concepts
- Function: The unit of execution — your code packaged as a ZIP file or container image
- Event: The input data that triggers your function (JSON payload)
- Runtime: The language environment (Node.js, Python, Java, Go, .NET, Ruby, custom)
- Execution role: The IAM role that grants your function permissions to access AWS resources
- Trigger: The service or resource that invokes your function
- Concurrency: The number of simultaneous executions your function can handle
Why Lambda Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Lambda fundamentally changes how you build and operate applications. Here's why it has become a cornerstone of modern cloud architecture:
No server management
You never patch an OS, update a runtime, or worry about a failing EC2 instance. AWS handles all infrastructure concerns — availability, scaling, and maintenance — transparently. Your operations burden shifts from managing machines to managing code.
Granular cost model
With Lambda you pay per request and per millisecond of execution. There is no idle cost. A function that runs 100ms per invocation and receives 1 million requests costs a fraction of running even the smallest EC2 instance 24/7. For sporadic workloads, the savings are dramatic.
Automatic scaling
Lambda handles scale-out transparently. If 1,000 events arrive simultaneously, Lambda provisions 1,000 execution environments concurrently — up to your account limits. When traffic subsides, capacity scales back in. There is no warm-up, no load balancer configuration, no auto-scaling group tuning.
Event-driven architecture enabler
Lambda is the glue of the AWS ecosystem. It connects services effortlessly — process S3 uploads, react to DynamoDB stream changes, handle SNS notifications, or chain Step Functions workflows. This lets you build loosely coupled, composable systems that evolve independently.
Complete Setup and Configuration Guide
Prerequisites
- An AWS account with billing enabled
- AWS CLI installed and configured (
aws configure) - Python 3.9+ or Node.js 18+ installed locally (for local development)
- An IAM user with permissions to create Lambda functions and IAM roles
Step 1: Create an Execution Role
Every Lambda function needs an IAM role that defines what it can do. At minimum, the role must trust Lambda and allow writing logs to CloudWatch. Here's how to create it via the AWS CLI:
# Create the trust policy document that allows Lambda to assume the role
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
# Create the role
aws iam create-role \
--role-name lambda-basic-execution-role \
--assume-role-policy-document file://trust-policy.json
# Attach the AWSLambdaBasicExecutionRole managed policy
# This grants CloudWatch Logs write permissions
aws iam attach-role-policy \
--role-name lambda-basic-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
The AWSLambdaBasicExecutionRole managed policy grants permissions to create log groups, log streams, and put log events. For production functions that access S3, DynamoDB, or other services, you'll need to add custom policies.
Step 2: Write Your Function Code
Let's create a Python function that processes an API Gateway event, fetches data from an external service, and returns a formatted response. This is a realistic handler pattern:
# lambda_function.py
import json
import os
import logging
from datetime import datetime
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
"""
Event: API Gateway proxy integration event
Context: Runtime information about the execution environment
"""
logger.info(f"Request received at {datetime.utcnow().isoformat()}")
logger.info(f"Event: {json.dumps(event, default=str)}")
# Extract request details from API Gateway event
http_method = event.get('httpMethod', 'GET')
path = event.get('path', '/')
query_params = event.get('queryStringParameters', {}) or {}
body = event.get('body', None)
# Parse body if present
parsed_body = {}
if body:
try:
parsed_body = json.loads(body)
except json.JSONDecodeError:
parsed_body = {'raw': body}
# Business logic — here we simply echo and enrich
response_data = {
'message': 'Hello from Lambda',
'method': http_method,
'path': path,
'query_params': query_params,
'body': parsed_body,
'timestamp': datetime.utcnow().isoformat(),
'request_id': context.aws_request_id,
'function_name': context.function_name,
'memory_limit': context.memory_limit_in_mb,
'remaining_time_ms': context.get_remaining_time_in_millis()
}
# Return API Gateway-compatible response
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'X-Request-Id': context.aws_request_id
},
'body': json.dumps(response_data)
}
For Node.js developers, the equivalent handler looks like this:
// index.js
const { DateTime } = require('luxon');
exports.handler = async (event, context) => {
console.log(`Request received at ${DateTime.utc().toISO()}`);
console.log('Event:', JSON.stringify(event, null, 2));
const httpMethod = event.httpMethod || 'GET';
const path = event.path || '/';
const queryParams = event.queryStringParameters || {};
let parsedBody = {};
if (event.body) {
try {
parsedBody = JSON.parse(event.body);
} catch (e) {
parsedBody = { raw: event.body };
}
}
const responseData = {
message: 'Hello from Lambda (Node.js)',
method: httpMethod,
path: path,
queryParams: queryParams,
body: parsedBody,
timestamp: DateTime.utc().toISO(),
requestId: context.awsRequestId,
functionName: context.functionName,
memoryLimit: context.memoryLimitInMB,
remainingTimeMs: context.getRemainingTimeInMillis()
};
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'X-Request-Id': context.awsRequestId
},
body: JSON.stringify(responseData)
};
};
Step 3: Package the Function
Lambda accepts code as a ZIP file or a container image. For simple functions, ZIP packaging is fastest. You can include dependencies alongside your handler:
# For Python — create a deployment package with dependencies
mkdir lambda-package
cp lambda_function.py lambda-package/
cd lambda-package
# Install dependencies into the package directory
pip install requests -t .
# Create the ZIP file (from inside lambda-package/)
zip -r ../deployment.zip .
cd ..
# Verify the package contents
unzip -l deployment.zip | head -20
For Node.js, the process is similar:
# For Node.js — create deployment package
mkdir lambda-package
cp index.js lambda-package/
cd lambda-package
# Initialize and install dependencies
npm init -y
npm install luxon
# Create ZIP (include node_modules)
zip -r ../deployment.zip index.js node_modules/ package.json
cd ..
For larger projects or when you need native modules, consider using the AWS SAM CLI or CDK which handle packaging automatically.
Step 4: Create the Lambda Function
Now deploy the packaged code using the AWS CLI:
# Create the Lambda function
aws lambda create-function \
--function-name my-api-handler \
--runtime python3.11 \
--role arn:aws:iam::123456789012:role/lambda-basic-execution-role \
--handler lambda_function.lambda_handler \
--timeout 10 \
--memory-size 256 \
--environment-variables "ENVIRONMENT=production,LOG_LEVEL=INFO" \
--zip-file fileb://deployment.zip
# Expected output includes:
# - FunctionArn
# - FunctionName
# - State: Active
# - Version: $LATEST
Let's break down the configuration parameters:
- --runtime: The language runtime version. Choose from python3.11, nodejs18.x, java17, go1.x, dotnet6, ruby3.2, or provide a custom runtime ARN
- --role: The ARN of the IAM execution role created in Step 1
- --handler: The entry point — format is
filename.function_namefor Python orfilename.handlerfor Node.js (the exported function name) - --timeout: Maximum execution time in seconds (1–900). API Gateway has a 29-second integration timeout, so values above 30s require asynchronous invocation patterns
- --memory-size: Memory in MB (128–10240). CPU scales proportionally — at 256MB you get ~0.4 vCPU, at 1769MB you get 1 vCPU, at 10GB you get 6 vCPUs
- --environment-variables: Key-value pairs available at runtime via
os.environorprocess.env
Step 5: Invoke and Test
Test your function directly without any triggers to verify it works:
# Invoke synchronously with a test payload
aws lambda invoke \
--function-name my-api-handler \
--payload '{"httpMethod":"GET","path":"/test","queryStringParameters":{"filter":"active"},"body":null}' \
--cli-binary-format raw-in-base64-out \
response.json
# View the response
cat response.json
# Expected: {"statusCode":200,"headers":{"Content-Type":"application/json",...},"body":"{...}"}
# Check CloudWatch logs
aws logs describe-log-groups --log-group-name-prefix /aws/lambda/my-api-handler
# Tail recent logs
LOG_GROUP="/aws/lambda/my-api-handler"
LOG_STREAM=$(aws logs describe-log-streams \
--log-group-name $LOG_GROUP \
--order-by LastEventTime \
--descending \
--limit 1 \
--query 'logStreams[0].logStreamName' \
--output text)
aws logs get-log-events \
--log-group-name $LOG_GROUP \
--log-stream-name $LOG_STREAM \
--limit 20
Step 6: Configure Triggers
A trigger defines what invokes your function. Here are the most common trigger configurations:
API Gateway REST API trigger
# Create a REST API
API_ID=$(aws apigateway create-rest-api \
--name "MyAPI" \
--endpoint-configuration types=REGIONAL \
--query 'id' \
--output text)
# Get the root resource ID
ROOT_ID=$(aws apigateway get-resources \
--rest-api-id $API_ID \
--query 'items[0].id' \
--output text)
# Create a resource and GET method
RESOURCE_ID=$(aws apigateway create-resource \
--rest-api-id $API_ID \
--parent-id $ROOT_ID \
--path-part "data" \
--query 'id' \
--output text)
aws apigateway put-method \
--rest-api-id $API_ID \
--resource-id $RESOURCE_ID \
--http-method GET \
--authorization-type NONE
# Set Lambda as the integration target
aws apigateway put-integration \
--rest-api-id $API_ID \
--resource-id $RESOURCE_ID \
--http-method GET \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:my-api-handler/invocations
# Grant API Gateway permission to invoke the Lambda
aws lambda add-permission \
--function-name my-api-handler \
--statement-id apigateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:123456789012:$API_ID/*/*/*"
# Deploy the API
aws apigateway create-deployment \
--rest-api-id $API_ID \
--stage-name prod
echo "API URL: https://${API_ID}.execute-api.us-east-1.amazonaws.com/prod/data"
S3 bucket trigger
# Create a notification configuration on an S3 bucket
aws s3api put-bucket-notification-configuration \
--bucket my-upload-bucket \
--notification-configuration '{
"LambdaFunctionConfigurations": [
{
"LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-api-handler",
"Events": ["s3:ObjectCreated:*"],
"Filter": {
"Key": {
"FilterRules": [
{"Name": "prefix", "Value": "uploads/"},
{"Name": "suffix", "Value": ".json"}
]
}
}
}
]
}'
# Grant S3 permission to invoke the Lambda
aws lambda add-permission \
--function-name my-api-handler \
--statement-id s3-invoke \
--action lambda:InvokeFunction \
--principal s3.amazonaws.com \
--source-arn arn:aws:s3:::my-upload-bucket
CloudWatch Events (scheduled execution)
# Create a rule that triggers every 5 minutes
aws events put-rule \
--name "five-minute-trigger" \
--schedule-expression "rate(5 minutes)" \
--state ENABLED
# Add Lambda as the target
aws events put-targets \
--rule "five-minute-trigger" \
--targets "Id=1,Arn=arn:aws:lambda:us-east-1:123456789012:function:my-api-handler"
# Grant CloudWatch Events permission to invoke the Lambda
aws lambda add-permission \
--function-name my-api-handler \
--statement-id cloudwatch-invoke \
--action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:us-east-1:123456789012:rule/five-minute-trigger
Step 7: Configure VPC Access (Optional)
By default, Lambda runs in an AWS-managed VPC with internet access. To connect to resources in your own VPC (RDS, ElastiCache, internal APIs), attach the function to your VPC subnets and security groups:
# Update function to run inside your VPC
aws lambda update-function-configuration \
--function-name my-api-handler \
--vpc-config '{
"SubnetIds": ["subnet-abc12345","subnet-def67890"],
"SecurityGroupIds": ["sg-xyz98765"]
}'
# Important: When attached to a VPC, Lambda loses internet access
# unless you configure a NAT Gateway/Instance in your VPC
# For outbound internet, ensure your route tables include:
# 0.0.0.0/0 -> NAT Gateway (in a public subnet)
VPC-attached Lambda functions have additional considerations:
- Cold starts may be slightly longer due to ENI creation (though AWS has improved this significantly)
- Your subnets must have sufficient IP addresses — Lambda creates one ENI per unique security group/subnet combination, shared across executions
- For high-concurrency functions, calculate:
IPs needed ≈ (number of subnets × number of security groups × concurrent executions) / some sharing factor
Step 8: Configure Concurrency and Provisioned Concurrency
Concurrency controls how many simultaneous executions your function can handle. Without limits, Lambda scales up to account-level quotas (typically 1,000 concurrent executions per region).
# Set reserved concurrency — caps this function and reserves slots
# This also prevents this function from starving other functions in the account
aws lambda put-function-concurrency \
--function-name my-api-handler \
--reserved-concurrent-executions 50
# Set provisioned concurrency — pre-warms execution environments
# Eliminates cold starts for the specified number of instances
aws lambda put-provisioned-concurrency-config \
--function-name my-api-handler \
--qualifier $LATEST \
--provisioned-concurrent-executions 10
# Verify configuration
aws lambda get-provisioned-concurrency-config \
--function-name my-api-handler \
--qualifier $LATEST
# Expected output shows:
# - Requested: 10
# - Allocated: 10 (when fully provisioned)
# - Status: READY
Provisioned concurrency is billed differently — you pay for the pre-warmed compute regardless of usage. Use it for latency-sensitive production APIs where cold start latency (typically 200ms–2s depending on runtime and package size) is unacceptable.
Step 9: Environment Variables and Secrets
Lambda supports environment variables natively. For secrets, integrate with AWS Secrets Manager or SSM Parameter Store:
# Set environment variables directly
aws lambda update-function-configuration \
--function-name my-api-handler \
--environment-variables '{
"Variables": {
"DATABASE_HOST": "mydb.xyz.us-east-1.rds.amazonaws.com",
"DATABASE_PORT": "5432",
"DATABASE_NAME": "production",
"SECRET_NAME": "prod/database/credentials",
"REGION": "us-east-1"
}
}'
# In your function code, retrieve secrets at runtime:
# Python — retrieve secrets from Secrets Manager
import boto3
import os
from botocore.exceptions import ClientError
def get_secret():
secret_name = os.environ.get('SECRET_NAME')
region_name = os.environ.get('REGION', 'us-east-1')
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
except ClientError as e:
logger.error(f"Failed to retrieve secret: {e}")
raise
# Use in handler
def lambda_handler(event, context):
db_creds = get_secret()
# db_creds['username'], db_creds['password'], etc.
# ... rest of handler logic
For better performance, cache secrets in memory across invocations (outside the handler). Lambda execution environments are reused for warm starts, so initialization code runs once per container:
# Python — cache secrets outside handler for reuse across invocations
import json
import os
import boto3
from botocore.exceptions import ClientError
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Cached variables — initialized once per execution environment
_secret_cache = None
_secret_cache_time = None
CACHE_TTL_SECONDS = 300 # Refresh every 5 minutes
def get_secret_cached():
global _secret_cache, _secret_cache_time
now = datetime.utcnow()
if _secret_cache is not None and _secret_cache_time is not None:
if (now - _secret_cache_time).total_seconds() < CACHE_TTL_SECONDS:
return _secret_cache
# Fetch fresh secret
secret_name = os.environ.get('SECRET_NAME')
region = os.environ.get('REGION', 'us-east-1')
client = boto3.client('secretsmanager', region_name=region)
try:
response = client.get_secret_value(SecretId=secret_name)
_secret_cache = json.loads(response['SecretString'])
_secret_cache_time = now
logger.info("Secret cache refreshed")
return _secret_cache
except ClientError as e:
logger.error(f"Secret retrieval failed: {e}")
# Fall back to stale cache if available
if _secret_cache is not None:
logger.warning("Using stale cached secret")
return _secret_cache
raise
def lambda_handler(event, context):
db_creds = get_secret_cached()
# Use db_creds['username'], db_creds['password'], etc.
return {'statusCode': 200, 'body': 'Success'}
Step 10: Configure Logging and Monitoring
Lambda automatically sends logs to CloudWatch. To gain deeper observability, configure advanced logging and integrate with monitoring tools:
# Enable JSON structured logging in your function
# Python example with structured logging
import json
import logging
import sys
from datetime import datetime
class StructuredFormatter(logging.Formatter):
def format(self, record):
log_entry = {
'timestamp': datetime.utcnow().isoformat(),
'level': record.levelname,
'message': record.getMessage(),
'module': record.module,
'function': record.funcName,
'line': record.lineno,
'request_id': getattr(record, 'request_id', 'unknown')
}
if record.exc_info:
log_entry['exception'] = self.formatException(record.exc_info)
return json.dumps(log_entry)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(StructuredFormatter())
logger.handlers = [handler] # Replace default handler
# Set log retention policy (logs persist forever by default — you'll want a limit)
aws logs put-retention-policy \
--log-group-name /aws/lambda/my-api-handler \
--retention-in-days 14
# Enable Lambda Insights for enhanced metrics (CPU, memory, cold starts)
aws lambda update-function-configuration \
--function-name my-api-handler \
--tracing-config Mode=Active \
--monitoring-config '{"MonitoringMode": "Active"}'
Advanced Configuration
Dead Letter Queues and Destinations
When asynchronous invocations fail (after exhausting retries), Lambda can send the event to a dead letter queue (DLQ) or a destination. Destinations are the newer, more capable mechanism:
# Configure a destination for successful and failed async invocations
aws lambda put-function-event-invoke-config \
--function-name my-api-handler \
--maximum-retry-attempts 2 \
--destination-config '{
"OnSuccess": {
"Destination": "arn:aws:sqs:us-east-1:123456789012:success-queue"
},
"OnFailure": {
"Destination": "arn:aws:sns:us-east-1:123456789012:failure-topic"
}
}'
# For older-style DLQ (SQS or SNS only for failures):
aws lambda update-function-configuration \
--function-name my-api-handler \
--dead-letter-config '{
"TargetArn": "arn:aws:sqs:us-east-1:123456789012:dlq-queue"
}'
File Systems and EFS Integration
For workloads requiring persistent shared storage or large file access, mount an EFS filesystem:
# Create an EFS filesystem and mount target in your VPC
# Then attach it to the Lambda
aws lambda update-function-configuration \
--function-name my-api-handler \
--file-system-configs '[
{
"Arn": "arn:aws:elasticfilesystem:us-east-1:123456789012:file-system/fs-abc12345",
"LocalMountPath": "/mnt/data"
}
]' \
--vpc-config '{
"SubnetIds": ["subnet-abc12345"],
"SecurityGroupIds": ["sg-xyz98765"]
}'
# In your code, read/write to /mnt/data as a normal filesystem
# Python example:
# with open('/mnt/data/processed_files.txt', 'a') as f:
# f.write(f"{filename}\n")
Layers
Layers let you share code and dependencies across multiple functions. Extract common utilities, SDK wrappers, or large dependencies into layers:
# Create a layer ZIP with shared utilities
mkdir python
cp shared_utils.py python/
pip install requests -t python/
zip -r shared-layer.zip python/
# Publish the layer
LAYER_ARN=$(aws lambda publish-layer-version \
--layer-name shared-utilities \
--description "Shared utilities and requests library" \
--compatible-runtimes python3.10 python3.11 \
--zip-file fileb://shared-layer.zip \
--query 'LayerVersionArn' \
--output text)
# Attach the layer to your function
aws lambda update-function-configuration \
--function-name my-api-handler \
--layers "$LAYER_ARN"
# Now your function can import shared_utils directly
# from shared_utils import format_response
Environment Variable Encryption
Lambda encrypts environment variables at rest by default using an AWS-managed KMS key. For sensitive variables, use a customer-managed key:
# Create a KMS key
KMS_KEY_ID=$(aws kms create-key \
--description "Lambda environment variable encryption key" \
--key-usage ENCRYPT_DECRYPT \
--customer-managed \
--query 'KeyMetadata.KeyId' \
--output text)
# Create an alias for easier reference
aws kms create-alias \
--alias-name alias/lambda-env-key \
--target-key-id $KMS_KEY_ID
# Update function to use the custom key
aws lambda update-function-configuration \
--function-name my-api-handler \
--kms-key-arn "arn:aws:kms:us-east-1:123456789012:key/$KMS_KEY_ID"
# Grant Lambda service permission to use the key
# (This is typically handled automatically, but verify if you encounter access issues)
Best Practices
1. Optimize for cold starts
- Keep packages small: Minimize dependencies. Use Lambda layers for heavy libraries. Avoid bundling unnecessary files
- Choose the right runtime: Node.js and Python have the fastest cold starts (~200-400ms). Java and .NET are slower (~1-2s) but can be improved with tiered compilation settings
- Use provisioned concurrency for latency-critical production endpoints
- Initialize outside the handler: Database connections, SDK clients, and configuration loading should happen in the global scope to be reused across invocations
2. Handle failures gracefully
- Always set timeouts: Never rely on the default 3-second timeout. Match the timeout to your workload's expected duration with a buffer
- Implement retry logic for transient failures in external calls (use exponential backoff)
- Use destinations for asynchronous invocations to capture both success and failure events
- Log structured data: Emit JSON-formatted logs so CloudWatch Logs Insights queries are efficient
3. Secure your functions
- Least privilege IAM: Each function should have only the permissions it needs. Create per-function roles rather than sharing a broad role
- Never hardcode secrets: Use Secrets Manager or SSM Parameter Store with caching
- Validate input: Treat all event data as untrusted. Validate and sanitize before processing
- Enable code signing: Use AWS Signer to ensure only authorized code runs in production
4. Monitor and debug effectively
- Enable active tracing with AWS X-Ray to visualize the full request path across services
- Set CloudWatch alarms on errors, throttles, and duration spikes
- Use Lambda Insights for CPU, memory, and network metrics at the function level
- Log request IDs: The
context.aws_request_idis the correlation ID across all AWS services for that invocation
5. Right-size your configuration
- Memory is not just RAM: It determines CPU allocation. A function at 256MB gets ~0.4 vCPU; at 1769MB, 1 vCPU; at 10GB, 6 vCPUs. For CPU-bound work, increasing memory can reduce total execution time and cost
- Test different memory settings: Run your function at 256MB, 512MB, 1GB, 2GB and compare both duration and cost (duration × memory price per ms). The optimum often surprises you
- Reserved concurrency protects downstream: If your function calls a database with 10 connections, set reserved concurrency to prevent overwhelming it during traffic spikes
6. Design for idempotency
- Assume events can be delivered more than once: Lambda's distributed invocation model means retries and at-least-once delivery for asynchronous invocations
- Use event IDs for deduplication: Store processed event IDs in DynamoDB with a TTL to prevent duplicate processing
- Make side effects conditional: Check if the work has already been done before writing to databases, sending emails, or triggering downstream systems
Common Configuration Patterns
Pattern 1: Web API Backend
# Function configuration for a typical REST API handler
aws lambda create-function \
--function-name api-handler \
--runtime python3.11 \
--handler lambda_function.lambda_handler \
--timeout 5 \
--memory-size 512 \
--reserved-concurrent-executions 100 \
--provisioned-concurrent-executions 5 \
--environment-variables "ENVIRONMENT=production,LOG_LEVEL=INFO" \
--tracing-config Mode=Active \
--role arn:aws:iam::123456789012:role/api-handler-role
Pattern 2: Event Processor (SQS/SNS triggered)
# Function configuration for asynchronous event processing
aws lambda create-function \
--function-name event-processor \
--runtime python3.11 \
--handler event_processor.lambda_handler \
--timeout 60 \
--memory-size 1024 \
--reserved-concurrent-executions 20 \
--environment-variables "QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789012/tasks" \
--dead-letter-config '{"TargetArn":"arn:aws:sqs:us-east-1:123456789012:dlq"}' \
--role arn:aws:iam::123456789012:role/processor-role
Pattern 3: Scheduled Job
# Function configuration for a nightly batch job
aws lambda create-function \
--function-name nightly-report-generator \
--runtime python3.11 \
--handler batch.lambda_handler \
--timeout 900 \
--memory-size 3008 \
--reserved-concurrent-executions 1 \
--environment-variables "REPORT_BUCKET=reports-bucket,REGION=us-east-1" \
--role arn:aws:iam::123456789012:role/batch-role
Local Development and Testing
While you can iterate directly with the AWS CLI, local tooling accelerates development:
# Install AWS SAM CLI for local testing
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html
# Initialize a SAM project
sam init --runtime python3.11 --name my-lambda-app
# Local invoke with a test event
sam local invoke MyFunction --event events/test