Serverless workloads
Serverless removes the server from the delivery problem and replaces it with different problems: cold starts, concurrency limits, idempotency, and a cost model that punishes a badly designed loop.
Service map
| Layer | Services | Use |
|---|---|---|
| Compute | Lambda | Stateless functions triggered by events |
| API | API Gateway (REST, HTTP, WebSocket) | Exposure, throttling, authorisation |
| Storage | S3, DynamoDB | Unstructured data, key-value at scale |
| Messaging | SQS, SNS, EventBridge | Decoupling, fan-out, routing |
| Orchestration | Step Functions | Multi-step workflows, retries, human approval |
| Streaming | Kinesis, MSK | Real-time event processing |
| Relational | Aurora Serverless, RDS Proxy | SQL compatibility without connection exhaustion |
Reference architecture
Client
│
▼
API Gateway (WAF, throttling, auth)
│
▼
Lambda (stateless handler)
├──► DynamoDB (single-table design)
├──► S3 (objects)
└──► EventBridge ──► SQS ──► Lambda (async consumer)
│
└──► DLQ + alarm
Template
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs22.x
Architectures: [arm64]
Tracing: Active
LoggingConfig:
LogFormat: JSON
Environment:
Variables:
POWERTOOLS_SERVICE_NAME: example-service
Resources:
ApiFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/api.handler
MemorySize: 512
Timeout: 10
ReservedConcurrentExecutions: 50
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref Table
Events:
Api:
Type: HttpApi
Properties:
Path: /items
Method: post
AsyncFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/async.handler
MemorySize: 1024
Timeout: 60
Events:
Queue:
Type: SQS
Properties:
Queue: !GetAtt Queue.Arn
BatchSize: 10
FunctionResponseTypes: [ReportBatchItemFailures]
Queue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 360
RedrivePolicy:
deadLetterTargetArn: !GetAtt DLQ.Arn
maxReceiveCount: 3
DLQ:
Type: AWS::SQS::Queue
Properties:
MessageRetentionPeriod: 1209600
DLQAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Messages have reached the dead-letter queue
MetricName: ApproximateNumberOfMessagesVisible
Namespace: AWS/SQS
Dimensions:
- Name: QueueName
Value: !GetAtt DLQ.QueueName
Statistic: Maximum
Period: 300
EvaluationPeriods: 1
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
Design rules
Idempotency
Every write path is idempotent. Lambda retries asynchronous invocations twice by default, and API Gateway can retry; a non-idempotent handler turns a transient failure into duplicate data.
- Use a client-supplied idempotency key stored with a conditional write.
- Make handlers safe to re-run: upsert rather than insert, or check-then-write under a condition.
- For SQS batch consumers, report partial batch failures so only failed messages are retried.
Concurrency
| Control | Purpose |
|---|---|
| Reserved concurrency | Guarantees capacity for a critical function and caps its blast radius |
| Provisioned concurrency | Removes cold starts for latency-sensitive paths |
| Account concurrency limit | Monitored; a runaway function can exhaust the region's pool |
A function with no reserved concurrency shares the account pool. One misbehaving consumer can starve every other function in the account.
Timeouts
Set the function timeout below the upstream timeout. If API Gateway waits 29 seconds, the function must not run for 30. For queues, the visibility timeout must exceed the function timeout, or a message is redelivered while still being processed.
Cold starts
- Keep deployment packages small; tree-shake dependencies.
- Prefer arm64 for price-performance.
- Initialise SDK clients outside the handler.
- Use provisioned concurrency only where a measured latency target requires it — it changes the cost model.
Dead letters
Every asynchronous path has a dead-letter queue and an alarm on it. A queue without an alarm is a silent data loss mechanism.
Security
- Least-privilege execution role per function.
- No
*on action and resource in the same statement. - Environment variables for configuration; Secrets Manager or SSM for secrets, with the extension caching values rather than calling on every invocation.
- VPC-attached functions only where they must reach private resources; otherwise they use the public AWS service endpoints with no NAT cost.
- API Gateway authoriser on every non-public route; WAF on public endpoints.
- Dependency scanning and signed deployments in the pipeline.
Observability
- Structured JSON logs with a correlation id and the request id.
- X-Ray or OpenTelemetry tracing enabled.
- Metrics per function: invocations, errors, duration, throttles, concurrent executions, cold-start count.
- Alarms on error rate, throttle rate, p99 duration, DLQ depth, and age of the oldest message.
Cost
Serverless cost is a function of invocations × duration × memory, plus the per-request charges of the surrounding services.
| Lever | Effect |
|---|---|
| Right-size memory | Lambda CPU scales with memory; doubling memory can halve duration |
| Reduce invocation count | Batch, aggregate, or move work to a queue consumer |
| Avoid polling | Event-driven triggers instead of scheduled polls |
| Shorten duration | Move slow I/O out of the request path |
| Set retention | CloudWatch Logs retention is a common hidden cost |
| Avoid idle provisioned concurrency | Provisioned capacity is billed even when unused |