A warm start is an invocation served by an execution environment Lambda already initialized: code loaded, globals populated, connections open, no init phase on the request path. Keeping functions warm therefore means controlling how many pre-initialized environments exist when traffic arrives. Scheduled pings keep exactly one alive, provisioned concurrency reserves as many as you pay for, and SnapStart restores snapshots on supported runtimes. We measure first, fix the bundle second, and pay only for paths where latency costs money.
What actually survives between two Lambda invocations?
Everything initialized outside the handler survives: global variables, SDK clients, open database connections, and the /tmp filesystem. When Lambda routes a request to an existing environment, the handler runs immediately with that state intact. That reuse is the warm start: the init phase, runtime boot plus your top-level code, never runs on the request path.
The latency gap is why this matters. Warm invocations typically add single-digit milliseconds of platform overhead, while cold starts range from under a hundred milliseconds for a lean Node.js function to several seconds for a heavy Java or badly bundled one (orders of magnitude we still observe in 2026; your runtime and dependency graph dominate). The execution environment lifecycle documentation describes the phases precisely; the practical takeaway is that a cold start is nothing more than init landing on a user's request.
How long does AWS keep an environment warm?
There is no contractual answer, and any exact figure you read online is folklore. AWS keeps idle environments available for a period measured in minutes rather than hours, adjusted according to memory size, traffic patterns, and internal capacity management. Plan as if reuse were probable shortly after an invocation and never guaranteed.
Two consequences follow. First, environments are also recycled periodically even under continuous load, so warmth is an optimization you benefit from, not a state you own: every deploy, scale-out, or runtime update creates fresh environments that will cold-start. Second, anything you cache in globals must tolerate dying at any moment, and anything long-lived, database connections above all, must be validated or re-established in the handler rather than trusted blindly.
Do EventBridge warming pings actually work?
Partially, and that word carries the whole answer. A scheduled rule pinging your function every few minutes keeps exactly one execution environment warm. The moment two requests arrive concurrently, the second one cold-starts anyway, because one environment serves one request at a time. Warming pings fix the single-user demo, not the production p99.
They remain a reasonable tool for low-traffic internal services: an admin backoffice, a reporting endpoint, anything where concurrency rarely exceeds one and an occasional cold start is acceptable. Have the handler short-circuit on the ping payload so each ping costs milliseconds, and follow the EventBridge scheduled rule guide for the wiring:
# One rule, one ping: exactly one environment stays warm
aws events put-rule \
--name warm-reporting-fn \
--schedule-expression "rate(5 minutes)"
aws events put-targets \
--rule warm-reporting-fn \
--targets '[{
"Id": "warm-1",
"Arn": "arn:aws:lambda:eu-west-3:123456789012:function:reporting",
"Input": "{\"warmer\": true}"
}]'
Is your p99 dominated by cold starts you have never actually measured? Describe your system: a one-page diagnosis within 48 hours.
Get my diagnosis →What does provisioned concurrency actually buy?
Provisioned concurrency instructs Lambda to keep N fully initialized environments ready before requests arrive, configured on a published version or alias, never on $LATEST. Requests within that count skip init entirely; traffic beyond it spills over to on-demand environments that cold-start as usual. It is the only mechanism that removes cold starts under real concurrency.
The cost model is the part to understand before signing off. You pay an hourly rate for every reserved environment, invoked or not, plus a reduced duration rate when they do serve requests; the exact figures live on the Lambda pricing page. Paying for idle overnight capacity is the classic waste, which is why the provisioned concurrency documentation pairs it with Application Auto Scaling: scheduled actions raise the count before business hours and drop it at night, and target tracking around 70 percent utilization absorbs the rest.
Is SnapStart the cheaper alternative?
Sometimes. SnapStart captures a snapshot of an initialized environment and restores it on demand instead of re-running init, which cuts cold start latency sharply on supported runtimes. Java has it broadly at no extra charge; Python and .NET followed with caching and restore fees. Node.js support had still not shipped as of August 2026: check current docs.
The constraints matter as much as the speed-up. SnapStart applies to published versions only, is mutually exclusive with provisioned concurrency on the same function, caps ephemeral storage, and reuses one snapshot across many restores: unique IDs, credentials, and network connections must be created in the handler, not during init. Restores are fast but not free, so measure before assuming parity with a warm environment. Details sit in the SnapStart documentation.
How do you force a cold start for testing?
Change the function configuration or publish a new version: Lambda retires the existing environments and creates fresh ones, so the next invocation is guaranteed cold. Updating a throwaway environment variable is the cheapest trigger. This is how you measure cold starts deliberately instead of waiting for one to surface in production.
# Every configuration change means fresh environments for subsequent invocations
aws lambda update-function-configuration \
--function-name reporting \
--environment "Variables={COLD_MARKER=$(date +%s)}"
Then invoke and read the REPORT line in CloudWatch Logs: Init Duration only appears on cold invocations and is the number your latency budget must absorb. Repeat the force-and-invoke cycle enough times to see a distribution, not a single sample; that distribution, weighted by how often real traffic hits cold environments, is your p99 truth.
Which option should you pay for?
Measure first, spend last. Pull Init Duration from your REPORT lines, weight it by how often cold starts actually reach users, then exhaust the free fixes before the paid ones. In most systems the right first move is a smaller bundle and lazy initialization, covered in our cold start reduction guide, not a monthly line item.
| Approach | What it fixes | Cost | Limits |
|---|---|---|---|
| Warming ping (EventBridge) | Cold starts for a single concurrent user | Near zero: a few invocations per hour | Keeps exactly one environment warm; concurrency N still cold-starts |
| Provisioned concurrency | Cold starts up to the provisioned count, any runtime | Hourly charge per reserved environment, used or not, plus reduced duration rate | Versions and aliases only; idle cost; needs auto scaling to track traffic |
| SnapStart | Init time on supported runtimes (Java, Python, .NET) | No extra charge on Java; caching and restore fees on Python and .NET | Published versions only; exclusive with provisioned concurrency; restore is fast, not zero |
| Bundle optimization | Init duration itself, for every environment | Engineering time only | Shrinks cold starts, never removes them |
Reserve the paid options for user-facing, latency-critical paths: checkout, authentication, search-as-you-type. Queue consumers and batch jobs do not care about a second of init. If you want a second pair of eyes on that trade-off, it is exactly what our performance optimization service exists for.
Warm start checklist
- Read
Init DurationfromREPORTlines before changing anything. - Force cold starts with a config change to measure a distribution, not an anecdote.
- Shrink the bundle and lazy-load heavy dependencies first.
- Use warming pings only where concurrency rarely exceeds one.
- Put provisioned concurrency on aliases, with scheduled auto scaling, on user-facing paths only.
- Evaluate SnapStart before provisioned concurrency on Java, Python, and .NET.
- Re-validate connections in the handler: environments die without notice.