Lambda Cold Starts and How to Cut Them
Introduction¶
When Lambda needs a new execution environment (first invoke, scale-up, or after one is recycled), it does a cold start: download your code, start the runtime, run your initialization code, then invoke the handler. Warm invokes skip all of that. The question is how often it happens and how much it costs.
The phases¶
- Download — pull your deployment package / container image.
- Init the runtime — start the JVM / Node / Python interpreter.
- Init your code — everything outside the handler function: imports, SDK clients, DB connection setup, config loading. This is often the biggest chunk and the part you control.
- Invoke — run the handler (warm invokes start here).
Init has a 10-second cap and gets full CPU regardless of your memory setting (a quirk worth exploiting — see below).
What makes it worse¶
- Runtime: JVM and .NET have heavy startup; Node, Python, Go, Rust are light. Go/Rust cold starts can be ~10–50ms; a fat Spring Boot JAR can be several seconds.
- Package size: a 250MB zip vs a 5MB zip. Trim dependencies, tree-shake, don't bundle the AWS SDK if the runtime provides it.
- Container images vs zip: images can be larger but Lambda caches layers; usually comparable now, sometimes images win for big deps.
- VPC: historically brutal; since the 2019 rework, the ENI is set up out-of-band and the added cold-start latency is small (~tens of ms). Still, a Lambda in a VPC needs the ENI and route to whatever it calls.
- Heavy init code: importing all of pandas, creating 5 SDK clients, reading a big config from S3 synchronously.
- Low memory: memory setting also scales CPU. 128MB Lambda inits slowly; 1024MB+ is much faster (and often cheaper overall because it finishes sooner).
The levers¶
Right-size memory¶
More memory = more CPU = faster init and execution. Sweet spot is often 512MB–1769MB (1 full vCPU at 1769MB). Use AWS Lambda Power Tuning (a Step Functions app) to find the cost/speed optimum — smaller isn't cheaper if it runs 3× longer.
Trim the package and init¶
- Lazy-import heavy modules inside the handler if only some code paths need them.
- Reuse SDK clients and DB connections across invokes (declare them outside the handler so warm invokes reuse them).
- Don't fetch config from S3/SSM on every cold start — bake it in, or cache with a TTL.
Provisioned Concurrency¶
Keeps N environments initialized and warm, ready to invoke with no cold start. Costs money 24/7 for those N. Use for latency-critical synchronous APIs; combine with Application Auto Scaling to schedule/scale it. Doesn't help beyond N concurrent — spillover still cold-starts.
SnapStart (Java, and now Python/.NET)¶
Lambda takes a snapshot of the initialized environment after init and restores from it on cold start — skipping phases 2 and 3. Big win for JVM (cold starts drop from seconds to ~sub-second), free (no extra charge beyond a small cache cost). Caveats: uniqueness (don't cache randomness/connections at init — use runtime hooks to re-establish), and it's per-published-version.
Keep-warm pings¶
A CloudWatch scheduled event invoking the function every few minutes keeps one environment warm. Cheap hack for low-traffic functions; doesn't help scale-up. Provisioned Concurrency is the real answer.
Measuring¶
# CloudWatch: the Init Duration is only logged on cold starts
filter @type = "REPORT"
| stats count() as invocations,
count(@initDuration) as cold_starts,
avg(@initDuration) as avg_init,
pct(@duration, 99) as p99_duration
Cold-start rate (cold / total) matters as much as the duration. A function at steady load has <1% cold starts; a spiky one can be 20%+.
Verification and troubleshooting¶
- p99 latency spikes, p50 fine — cold starts. Check
@initDurationin logs; the spikes correlate with scale-up events. - Every invoke is cold — very low traffic (environments recycled between calls), or you're publishing a new version on every deploy and traffic hasn't warmed it. Provisioned Concurrency, or accept it for async work.
- SnapStart didn't help / broke things — the function caches a DB connection
or random seed at init; use the
afterRestoreruntime hook to re-init those. - VPC Lambda slow to reach RDS — not usually cold start anymore; check the security group, that the subnets have a route (NAT for internet, or VPC endpoints), and connection reuse (RDS Proxy).
- Provisioned Concurrency not eliminating cold starts — traffic exceeds the provisioned count; the overflow cold-starts. Scale PC with a schedule or a utilization target.
- Big package, slow download —
du -shthe deployment; remove dev deps,.git, tests; use Lambda layers for shared deps; consider ARM (Graviton) — often faster and cheaper.
Related tools and reading¶
- On-site: Timestamp converter.
- Related posts: SQS vs SNS vs EventBridge, CloudWatch Logs and metric filters.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Cloud & AWS
AWS WAF: The Rules That Matter
AWS WAF filters HTTP requests at the ALB/CloudFront/API Gateway edge. Here's a sensible baseline Web ACL, how to tune with count mode, and the false-positive traps.
October 24, 2026An S3 Static Site Behind CloudFront
The right way to host a static site on AWS: a private S3 bucket, CloudFront with OAC in front, HTTPS via ACM, and SPA fallback routing. Plus the 403s that trip everyone.
October 24, 2026CloudWatch Logs and Metric Filters
CloudWatch Logs is cheap to write and expensive to forget about. Here's retention, turning error patterns into metrics and alarms, and Logs Insights for ad-hoc queries.
October 24, 2026