Lambda: Performance, Concurrency & Cold Starts
Cold Start Anatomy
A cold start happens when Lambda initializes a new execution environment. The INIT phase: download code/container → start runtime → run module-level code. Can add 100ms–3s depending on runtime and code size.
Phase | What happens | When
---------------|----------------------------------------|---------------------------
Download | Fetch deployment package from S3/ECR | Cold start only
Runtime init | Start Node.js/Python/JVM process | Cold start only
Handler init | Run module-level code (your init code) | Cold start only
Invoke | Run handler function | Every invocation
Typical cold start durations (simple function, no VPC):
Node.js: 50-200ms
Python: 100-300ms
Java: 1-5s (JVM startup) → use SnapStart
Go: <50ms (compiled binary)
Container: 1-10s (image pull)Reducing Cold Starts
Provisioned Concurrency: keep N environments pre-initialized, no cold starts for those
SnapStart (Java only): snapshot JVM state after init, restore snapshot on cold start — 90% faster
Right-size memory: more memory = faster network/CPU, faster INIT phase
Minimize package size: smaller ZIP = faster download; use tree-shaking, remove unused deps
Avoid VPC unless needed: VPC adds ~100ms for ENI attachment (improved with Hyperplane ENI)
Move init code outside handler: DB connections, SDK clients, parsed config
# Provisioned Concurrency (warm environments always ready)
aws lambda put-provisioned-concurrency-config \
--function-name my-api-handler \
--qualifier v5 \
--provisioned-concurrent-executions 10
# Scale Provisioned Concurrency with Application Auto Scaling
aws application-autoscaling register-scalable-target \
--service-namespace lambda \
--resource-id function:my-api-handler:v5 \
--scalable-dimension lambda:function:ProvisionedConcurrency \
--min-capacity 5 \
--max-capacity 50Concurrency Model
Concurrency = (invocations/second) × (avg duration in seconds)
Types:
Unreserved concurrency: shared pool for all functions in the region (default 1000)
Reserved concurrency: cap a function to N — never exceeds this, guaranteed minimum
Provisioned concurrency: pre-warmed environments (subset of reserved)
Account default: 1000 concurrent executions per region
Request increase: via Service Quotas console (up to tens of thousands)
Throttling (429):
Sync invocations: error returned immediately to caller
Async invocations: retried automatically 2 times with backoff, then DLQ/destination# Set reserved concurrency (also limits function to 100 max)
aws lambda put-function-concurrency \
--function-name my-api-handler \
--reserved-concurrent-executions 100
# Set to 0 to completely throttle function (emergency disable)
aws lambda put-function-concurrency \
--function-name my-api-handler \
--reserved-concurrent-executions 0ARM64 (Graviton2)
Lambda supports ARM64 architecture (AWS Graviton2). Up to 34% better price/performance for many workloads. Simple runtime change — no code changes for Node.js/Python.
aws lambda update-function-configuration \
--function-name my-api-handler \
--architectures arm64Lambda Power Tuning
Open source tool (AWS Step Functions): runs your function at 10 different memory sizes, finds optimal cost/performance
Use it before choosing memory settings for production
Often: doubling memory doubles cost per second but halves duration → same or cheaper total cost
Run at: github.com/alexcasalboni/aws-lambda-power-tuning
Ephemeral Storage (/tmp)
import { writeFileSync, readFileSync, existsSync } from 'fs';
// /tmp is shared across warm invocations in the same environment
// Cache expensive downloads across invocations
const CACHE_PATH = '/tmp/model-cache.bin';
export const handler = async (event) => {
if (!existsSync(CACHE_PATH)) {
// Cold start: download and cache
const modelData = await downloadModel();
writeFileSync(CACHE_PATH, modelData);
}
const model = readFileSync(CACHE_PATH);
return predict(model, event.input);
};Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free