cold-start-budget-reference
Pure-reference catalog of latency budgets across serverless runtimes: cold starts AND timeouts. Covers AWS Lambda's three-phase cold start (Init: download+unzip+runtime-bootstrap; Init code: imports + module load; Invoke: handler execution), Cloudflare Workers' isolate model (sub-millisecond cold starts via V8 isolates per developers.cloudflare.com), Vercel Edge Runtime, Lambda SnapStart for JVM (snapshot-restore for Java), and provisioned-concurrency trade-offs, plus Lambda timeout + billing budgets in references/timeout-budgets.md (the 15-minute hard limit, getRemainingTimeInMillis, per-ms billing, memory-CPU scaling, the API Gateway 29s / SQS visibility-timeout integration cascade). Use when designing latency or timeout budgets, choosing a runtime, sizing memory, or auditing cold-start variance in production.
Install with skills.sh (any agent)
npx skills add testland/qa --skill cold-start-budget-referencecold-start-budget-reference
Overview
Per aws.amazon.com/blogs/compute on cold starts (opens in new window), Lambda's cold start has three phases:
Phases 1 + 2 are the "cold" part. Phase 3 is what runs every invocation (cold or warm).
When to use
How to use
Per-runtime cold-start budgets
Per AWS, Cloudflare, Vercel docs (typical ranges; bigger packages and bigger memory-class skew higher):
| Runtime | Typical cold start | Architecture |
|---|---|---|
| AWS Lambda Node.js (256MB) | 200-700ms | Container (Firecracker microVM) |
| AWS Lambda Python (256MB) | 250-800ms | Container |
| AWS Lambda Java 11 (512MB, no SnapStart) | 1.5-6s | Container + JVM warmup |
| AWS Lambda Java 11 (512MB, SnapStart) | 100-300ms | Snapshot-restore per docs.aws.amazon.com/lambda (opens in new window) |
| AWS Lambda .NET (1GB) | 1-3s | Container + .NET runtime |
| AWS Lambda Go (256MB) | 100-300ms | Container; pre-compiled binary |
| AWS Lambda Rust (256MB) | 50-200ms | Container; pre-compiled binary |
| Cloudflare Workers | 0-5ms (V8 isolate spawn) | V8 isolate per developers.cloudflare.com/workers (opens in new window) |
| Vercel Edge Runtime | 5-30ms | V8 isolate (similar to Workers) |
| Vercel Node.js Functions | 200-500ms (small) to 2-3s (large) | Lambda under the hood |
| Netlify Functions | 300ms-2s | Lambda under the hood |
The "Workers / Edge" qualitative leap is the isolate model: each function is a V8 isolate, spun up in microseconds per developers.cloudflare.com (opens in new window) - no container, no OS startup.
Mitigations
Provisioned concurrency (AWS Lambda)
Per docs.aws.amazon.com/lambda (opens in new window): keeps N execution environments pre-initialised. Eliminates cold starts up to N concurrent requests; you pay for the keep-warm time.
Trade-off: cost. A constant N=10 provisioned concurrency for 30 days ≈ $30-300 depending on memory class.
Lambda SnapStart (Java / .NET)
Per docs.aws.amazon.com/lambda/latest/dg/snapstart.html (opens in new window): takes a snapshot of the initialised JVM and restores from it on each cold start. Reduces Java cold starts from 1.5-6s → 100-300ms.
Caveats:
Package-size discipline
Lambda cold-start scales with deployment-package size. Per AWS: keep under 50MB (zipped) → cold start in the 200-800ms range. Larger packages → seconds.
Avoid heavy module-level imports
Init code runs once but on every cold start. Heavy imports (database connection pool init, large dependency trees) inflate init time.
# Bad: top-level
import heavy_lib # 2s import time
def handler(event, ctx):
return heavy_lib.do_thing(event)
# Better: lazy-import
def handler(event, ctx):
import heavy_lib
return heavy_lib.do_thing(event)Lazy imports add per-warm-call latency but reduce cold-start spike.
Runtime choice
Pre-compiled runtimes (Go, Rust) have far lower cold starts than managed runtimes (Java, Python). For latency-critical paths, runtime choice is a primary lever.
Timeout budgets
Cold start is the front of the latency budget; the back is the timeout. Lambda's 15-minute hard limit, the getRemainingTimeInMillis early-return pattern, per-ms billing and the memory-CPU relationship, and the integration timeout cascade (API Gateway's hard 29s, SQS visibility timeouts, Lambda@Edge limits) are cataloged in references/timeout-budgets.md.
Testable behaviours
| Behaviour | Test |
|---|---|
| Cold start within budget | Force cold (deploy or wait > idle-evict time); measure p95 first-invocation latency |
| Warm performance | Subsequent invocations (50+) → p95 well within prod budget |
| SnapStart effective | Pre/post SnapStart cold start delta |
| Provisioned concurrency keeps warm | Run for an hour; no cold-start spikes observed |
| Package size in budget | Build-step assertion: zipped artifact < 50MB |
| No heavy init-time imports | Profile init phase; assert < 500ms |
Worked example
A Java 11 Lambda (512MB) serves a low-traffic checkout callback. Users report occasional multi-second waits, though the dashboard p95 looks healthy.
Result: the cold-start tail drops from ~5s to the 100-300ms range with no provisioned-concurrency spend.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| p99 latency surprise | Cold starts at the tail; not visible in p50/p95 | Watch p99; explicit cold-start monitoring (CloudWatch Init Duration metric) |
| Large dependency tree on init path | Cold start inflated 2-5x | Audit imports; lazy-import non-critical |
| Java on Lambda without SnapStart | 5s cold starts | Enable SnapStart |
| Provisioned concurrency without size analysis | Pay for unused warm instances | Tune to actual concurrency p99 |
| Cold-start test only on the local dev environment | Local doesn't simulate Lambda init | Deploy + test against AWS / Workers / Edge |
| Treat cold starts as "rare" | Bursty traffic → cold starts cluster | Account for both steady-state and burst patterns |
| Ignore module bundling | Webpack-bundled is smaller AND has fewer import resolution hops | Bundle for production Lambdas |
Limitations
References
AWS Lambda timeout + billing budgets
View source (opens in new window)AWS Lambda timeout + billing budgets
AWS Lambda's wall-clock limit is 15 minutes (900 seconds) per invocation. Per docs.aws.amazon.com/lambda configuration-timeout (opens in new window): "The default value for this setting is 3 seconds, but you can adjust this in increments of 1 second up to a maximum value of 900 seconds (15 minutes)." For longer work use Step Functions, AWS Batch, or ECS Fargate - don't architect around the limit.
Timeout vs deadline at runtime
Per docs.aws.amazon.com/lambda python-context (opens in new window), the Context object exposes get_remaining_time_in_millis() (Python) / getRemainingTimeInMillis() (Node/JVM). Break out proactively:
def handler(event, context):
while not_done:
if context.get_remaining_time_in_millis() < 5000:
save_checkpoint()
return {"status": "partial", "checkpoint": ...}
do_work_chunk()
return {"status": "complete"}The 5-second cushion lets the handler return cleanly; without it the Lambda is force-killed at timeout (no SIGTERM grace) and the caller gets a 504-equivalent.
Billing semantics
Per docs.aws.amazon.com/lambda lambda-pricing (opens in new window):
| Cost component | Detail |
|---|---|
| Request charge | $0.20 per 1M requests (us-east-1) |
| Compute charge | Memory-class × GB-seconds (billed per ms) |
| Init duration | Free; not billed (per AWS; historically has changed - verify current docs) |
GB-second formula: memory_GB * duration_seconds. A 512MB Lambda running 1000ms costs 0.5 * 1.0 * $0.0000166667 = $0.00000833; per million 1s invocations at 512MB: ~$8.54 including the request charge.
Memory ↔ CPU relationship
Per docs.aws.amazon.com/lambda configuration-memory (opens in new window): "The amount of CPU available to a function is proportional to the memory you allocate to it. At 1,769 MB, a function has the equivalent of one vCPU." Compute-bound workloads should size memory by CPU need; the sweet spot is usually the memory class where wall-clock time stops dropping (often 1024-2048MB).
Integration timeout cascade
The integration's timeout is often the operational ceiling:
| Integration | Timeout | Lambda config |
|---|---|---|
| API Gateway (REST + HTTP API) | 29 seconds (hard) | Lambda timeout MUST be < 29s |
| Application Load Balancer | 4s default; configurable to 4000s | Configurable both sides |
| CloudFront (Lambda@Edge) | 5s viewer functions; 30s origin | Tight viewer limit |
| SQS (event source) | Per-queue visibility timeout (default 30s) | Visibility > Lambda timeout × 6 (AWS recommendation) |
| DynamoDB Streams | 6h batch window | Per-batch limit |
| EventBridge (async) | Retries on timeout | Idempotency required |
| Step Functions | Per-task timeout; default 60s | Per-task tuning |
The API Gateway 29-second hard limit is the most-encountered surprise: a Lambda configured for 60s still times out at 29s because API Gateway gives up first.
Testable behaviours
| Behaviour | Test |
|---|---|
| Completes within timeout under prod load | k6 / load run against the deployed function |
| Graceful return via remaining-time check | Inject slowness; assert "partial", not 504 |
| API Gateway 29s budget honoured | Long-running endpoint via API GW URL; assert ≤ 29s |
| SQS visibility > Lambda timeout | Force a timeout; observe SQS re-delivery |
| Memory sweet spot found | Run at 256/512/1024/2048 MB; chart duration |
| Cost at p99 within budget | Latency × memory × invocations × price/GB-s at p99, not average |
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Timeout = 900s "for safety" | Stuck Lambdas burn 15min; concurrency limits hit | Match timeout to p99 + buffer |
| Lambda timeout > API Gateway's 29s | API GW kills first; Lambda runs unused | Timeout < 29s behind API GW |
| SQS visibility < Lambda timeout | In-flight message re-delivered → duplicates | Visibility > timeout × 6 |
| No remaining-time check | Force-kill; no progress saved | Check + early-return |
| Sizing memory by RAM need only | Compute-bound work wastes wall-clock | Tune by p95 duration |
| Cost calculated from the average | p99 spikes blow the budget | Calculate against p99 |
Notes
References
Related skills
aws-sam-local-testing
Wraps AWS SAM (Serverless Application Model) Local CLI for testing Lambda functions locally: `sam local invoke` (single invocation with event payload), `sam local start-api` (local API Gateway emulator), `sam local start-lambda` (local Lambda invoke endpoint for AWS SDK clients), and event-payload generation (`sam local generate-event`). For C#/.NET Lambdas, references/dotnet.md covers handler-direct testing with Amazon.Lambda.TestUtilities (TestLambdaContext, TestLambdaLogger) and the dotnet-lambda CLI. Use when testing Lambda + API Gateway + integrated AWS services locally.
azure-functions-tests
Runs Azure Functions locally using Azure Functions Core Tools v4 (`func start`), Azurite storage emulation, and framework-native unit tests for handler code (.NET isolated worker model, Node.js v4, Python v2). Covers HTTP, queue, and timer trigger testing, admin-endpoint invocation for non-HTTP triggers, and binding verification via local.settings.json. Use when testing Azure Functions before deployment, reproducing trigger behaviour without live Azure services, or gating function handler logic in CI.
cloudflare-workers-miniflare
Wraps Miniflare 3 (the official Cloudflare Workers simulator) and Wrangler dev for testing Workers locally. Covers Miniflare's getMiniflare() programmatic API (workerd-backed simulation matching prod), the wrangler dev local-mode (live-reload during dev), KV / Durable Objects / R2 / D1 bindings emulation, and Vitest + @cloudflare/vitest-pool-workers for in-process tests. Use when testing Cloudflare Workers code locally.
serverless-integration-test-builder
Workflow-driven skill that builds the integration-test suite for a serverless application from its IaC definition (SAM template / serverless.yml / Wrangler config / Vercel functions / Netlify functions). Walks through: identifying the function inventory + event sources, picking the right local-emulator per function (sam local / Miniflare / netlify dev / vercel dev / serverless-offline), generating test events per event source, asserting on cold-start + timeout budgets, and emitting the test directory + CI config. Use when introducing integration tests to a serverless project.