What is an AWS Lambda Cold Start? (Causes & 5 Fast Fixes)

Logeshwaran.C

A cold start is the extra bit of time AWS Lambda needs the first time your function runs on a brand-new copy of its environment — it has to fetch your code, boot the runtime, and run your setup code before it can even look at the request. Most cold starts finish in under a second, and AWS's own analysis of production workloads puts them in under 1% of invocations — but here's the part almost nobody tells you: since August 1, 2025, you pay for that waiting time. It used to be free.

⚡ Quick Answer

What it is → The one-time setup delay before Lambda can run your handler for the first time on a fresh execution environment.

How long → Typically under 100 ms to just over 1 second, but Java/.NET with heavy frameworks can run several seconds.

Fastest cheap fix → Trim your deployment package and reuse SDK clients — see the cheap fixes first.

If cold starts are actually hurting a customer-facing feature, skip straight to SnapStart or Provisioned Concurrency.

Jake's Tuesday afternoon demo

Jake runs a small phone repair and resale shop, and last month he had a developer build him a tiny trade-in estimator: a customer types in their phone's condition, it hits a backend, and a price pops up. It runs on AWS Lambda. It's cheap, it scales, and Jake doesn't have to think about servers — right up until the Tuesday afternoon he demoed it to a walk-in customer who'd been on the fence about trading in her iPhone.

She tapped "Get my price." Nothing happened for about four seconds. Then the number appeared. By then she'd already put her phone back in her bag and said she'd "think about it." Jake never saw her again.

‍♂️ Jake's Reality Check

"It worked fine when the developer showed me. Then it worked fine for the first customer that day. Why did it choke on the second one, four hours later?"

Because Lambda had gone to sleep in between. If nobody hits your function for a while, AWS quietly recycles the little sandbox it was running in. The next request has to build a brand-new one from scratch — and that rebuild is exactly what she waited through.

Jake called Ethan that evening. Ethan has spent a decade patching Windows fleets and now spends half his week untangling AWS bills for small businesses like Jake's, and he put the problem in terms Jake could picture immediately.

"It's like your shop," Ethan said. "If a customer walks in during business hours and you're standing at the counter, you help them instantly. That's a warm start. But if they knock on the door at 6 AM before you've unlocked, turned on the lights, and booted the register, there's a delay while you get set up. That delay is the cold start. Lambda isn't broken — it's just not staffed 24/7 unless you pay it to be."

That's the whole idea in one sentence. But the details — why it happens, how long it actually takes, what you can do about it, and the one thing that changed under everyone's feet last year — are worth getting exactly right, because the popular advice on this topic is a mix of outdated blog posts, over-engineering, and at least one persistent myth that AWS itself has already debunked.

What a cold start actually is, phase by phase

AWS Lambda is what's called serverless compute — a way of running code where you never provision or manage a server yourself. The word "serverless" doesn't mean there's no server; it means AWS handles the server for you, spinning one up only when something needs to run and tearing it down when it's idle. That trade-off — no idle server sitting around costing you money 24/7 — is exactly what makes cold starts possible in the first place.

Every time your function is invoked, it runs inside something Lambda calls an execution environment. Think of it as a small, isolated sandbox: your code, your chosen runtime (Node.js, Python, Java, and so on), and the memory you configured, all packaged together. Every invocation moves through three phases: Init, Invoke, and Shutdown.

  1. Init phase. Lambda downloads your function's code from internal storage (or from Amazon ECR if you deployed it as a container image), starts the execution environment with the memory and runtime you configured, and then runs any of your code that sits outside the main handler — things like creating a database client, loading a configuration file, or initializing an SDK.
  2. Invoke phase. Lambda actually calls your handler function with the event data, and your business logic runs.
  3. Shutdown phase. Eventually, once Lambda decides the environment is no longer needed, it's frozen or torn down.

The cold start is that first phase — downloading the code and getting the environment ready. It happens once per execution environment, not once per request. If the same environment handles a second request shortly after the first, the Init phase is skipped entirely and you go straight to Invoke. That's called a warm start, and it's dramatically faster because there's nothing left to set up.

✅ Why this matters more than the definition

The environment isn't destroyed the instant your function finishes. Lambda keeps it around for a while — AWS doesn't publish an exact number and calls the retention period "non-deterministic" — specifically so the next request can skip the cold start entirely. This is why a function that gets hit constantly almost never feels slow, while one that gets hit once an hour feels slow every single time.

The five situations that trigger one

People tend to think of cold starts as something that only happens after a long nap. That's one cause, but it's not the only one — and missing the other four is exactly why so many people "fix" cold starts and then get confused when they come back.

1. The first invocation ever

The very first time a newly deployed function is called, there is obviously no warm environment sitting around waiting. It has to be built from nothing.

2. A quiet period, then a request

This is Jake's shop scenario. If nothing invokes the function for a stretch of time, Lambda eventually recycles the environment to free up capacity. The exact idle window isn't published and varies by account and region, but low-traffic functions — internal tools, admin dashboards, anything a customer only touches occasionally — feel this constantly.

3. Traffic scaling up

This is the one most people miss. Every concurrent invocation needs its own execution environment. If your function is already handling 10 requests at once and an 11th comes in at the same moment, that 11th request gets a brand-new, cold environment — even though the other 10 are perfectly warm. The rule is blunt: every additional concurrent invocation gets a cold start, even while existing concurrent executions stay warm. A sudden spike in traffic, not idle time, is often the real reason a well-used production function still shows cold starts in its metrics.

4. You deployed new code or changed configuration

Any time you update a function's code, or change something like its memory, environment variables, or layers, the next invocation is guaranteed to be a cold start. Lambda has to retire the environments running the old version so nobody accidentally executes stale code, and the replacement has to be built fresh. If you deploy several times a day, you are manufacturing cold starts on purpose — which is fine, you just shouldn't be surprised by them.

5. Internal load rebalancing

This one is the least intuitive: because Lambda spreads your function across multiple Availability Zones and load-balances traffic behind the scenes, a function can be invoked twice in quick succession and cold-start both times, purely because of internal rebalancing that has nothing to do with your code or your traffic pattern. You can't engineer around this one. It's just how a highly available, multi-tenant service behaves.

⚠️ What "keep it warm" advice actually breaks

A lot of older tutorials tell you to ping your function every few minutes with a scheduled EventBridge rule to "keep it warm." It's a common community workaround — even AWS's performance guidance acknowledges it — but it only ever keeps one environment warm. If your real traffic needs five concurrent environments and your ping only ever triggers one, the other four still cold-start the moment real traffic shows up. Pinging also costs you invocation charges for work that produces nothing, and it can mask a scaling problem instead of solving it.

How long a cold start actually lasts

AWS has analyzed production Lambda workloads directly, and the numbers it found are worth taking seriously: cold start duration varies from under 100 ms to over 1 second, and cold starts occur in under 1% of invocations for typical production traffic. That 1% figure sounds tiny until you remember what it means in practice — if you're running a checkout flow that handles 50,000 requests a day, roughly 500 of those customers could be sitting through an extra second of silence, every single day, forever.

The actual number for your function depends heavily on what you built it with. This is one of the most consistent patterns in the Lambda world, and it's worth being honest about it rather than pretending every language behaves the same.

Runtime family Typical cold start behavior Why
Python, Node.js, Go Fast — usually well under a second Lightweight interpreters or compiled binaries with minimal startup ceremony
Java, .NET (uninitialized JVM/CLR) Slow — can run into multiple seconds for framework-heavy apps Class loading, dependency injection, and framework bootstrapping (Spring Boot is the classic offender) add real work before your code even runs
Container image functions Can be slower than an equivalent ZIP package Larger images take longer to pull and unpack before Init can even begin, though SnapStart now extends to container images too

 What changed — and why almost nobody has noticed yet

  • Before August 1, 2025: for on-demand Lambda functions packaged as ZIP files using managed runtimes, the time spent in the Init phase was not billed. You paid only for the Invoke phase.
  • Since August 1, 2025: AWS standardized billing for the Init phase across every runtime type, deployment package, and invocation mode. That cold start you weren't paying for now shows up as extra Billed Duration on every invocation that needs one.
  • What that means for you: your CloudWatch Billed Duration numbers went up on that date even if your code didn't change at all, and low-traffic functions — the ones with the most cold starts relative to total invocations — feel it the most.

This is the reveal that most articles on this topic still get wrong, because most of them were written before mid-2025 and never got updated. If you read an older post confidently telling you "the good news is you don't pay for cold start time," that sentence used to be true and now it just isn't, for anyone running Lambda in a way that could hit a cold start at all. AWS announced the change on its compute blog, and it applies regardless of which language or framework you're using.

What a cold start actually costs you now

Lambda's standard pricing has two parts: a per-request charge and a duration charge measured in GB-seconds, where a GB-second is one gigabyte of allocated memory held for one second. On x86, the published rates are $0.20 per million requests and $0.0000166667 per GB-second for the first pricing tier, with Arm-based (Graviton) functions running roughly 20% cheaper on the duration side. Every AWS account also gets a free tier of 1 million requests and 400,000 GB-seconds per month that doesn't expire.

Since the Init phase change, a cold start's duration is folded straight into that Billed Duration number. Say your function is configured with 1 GB of memory and a cold start adds 400 extra milliseconds beyond your normal execution time. That's roughly 0.0004 additional GB-seconds per cold-started invocation — not much on its own, but multiply it by every cold start you get in a month, and it becomes a real, visible line in your bill rather than a rounding error you never saw. One detail matters here: memory allocation also controls CPU. Lambda allocates CPU power in proportion to the memory you configure, and a function reaches the equivalent of one full vCPU at 1,769 MB of memory. That relationship affects cold starts too, because a memory-starved function does its Init-phase work — loading libraries, connecting clients — with less CPU available to do it quickly.

A worked example makes this easier to feel than just read about. Take a low-traffic internal tool: 20,000 invocations a month, 512 MB of memory, and a cold start on roughly 8% of those invocations because the tool sits idle for long stretches between uses. If each cold start adds 350 milliseconds of Init time on top of a normal 150-millisecond execution, that's 1,600 cold-started invocations a month, adding up to roughly 280 extra GB-seconds across the month once the memory factor is included. At the published x86 duration rate, that's a few cents by itself — small enough to vanish inside anyone's free tier. The number that actually matters isn't this month's few cents; it's that this line item didn't exist at all before August 2025, and a fleet of a few hundred similarly idle internal functions turns "a few cents each" into a real, recurring cost nobody budgeted for, because nobody had to before.

‍♂️ Jake's Reality Check

"So if I just crank the memory setting way up, my cold starts get faster and I dodge the extra charge?"

You get faster, not free. More memory means more CPU during Init, which usually shortens the cold start. But you're now paying the higher per-GB-second rate for that shorter duration, and the Init phase itself is still billed regardless of how fast it finishes. It's a real lever — just not a loophole.

The VPC cold-start myth that refuses to die

Search "Lambda cold start" for more than five minutes and you'll run into someone insisting you should never put a Lambda function inside a Virtual Private Cloud (VPC) — a private, isolated network you control inside AWS, commonly used so your function can reach a database that isn't exposed to the public internet — because it will "add ten seconds to every cold start." That advice was true. It was fixed in 2019.

Before AWS's networking overhaul, every VPC-enabled Lambda function needed its own dedicated Elastic Network Interface (ENI) — essentially a virtual network card — created and attached at cold start time. Creating and attaching an ENI was genuinely slow, and it counted against your account's ENI limits, so heavy VPC usage could hit hard caps too. The fix: a shared, Lambda-managed resource called a Hyperplane ENI, now created once, when the function is created or its VPC settings change, rather than once per execution environment. New execution environments connect to that existing Hyperplane ENI through a fast network tunnel instead of waiting for a new interface to be built from scratch. The improvement rolled out across all commercial regions starting in 2019.

✅ Why this is worth saying plainly

If your function needs to reach an RDS database, an internal API, or anything else that lives inside a VPC, putting it in that VPC is not the cold-start death sentence it used to be. You should still avoid a VPC when you don't need one, because it adds complexity (subnets, security groups, sometimes a NAT Gateway for outbound internet access) — but "it'll wreck my cold starts" hasn't been the reason to avoid it since 2019.

The cheapest fixes first

Before you reach for anything that costs extra money, there's a short list of things you can do to a function's code and configuration that shrink cold starts for free. None of these eliminate a cold start; they just make the Init phase do less work.

  1. Trim your deployment package. A smaller ZIP file (or a smaller container image) has less to download and unpack before Init can even begin. Remove unused dependencies, and if you only need one function from a large SDK, avoid pulling in the entire library where your language allows selective imports.
  2. Move work out of the global scope only when it belongs in the handler. Code that runs outside your handler — creating a database client, reading a config file — runs once per cold start and is reused on every warm invocation afterward, so this is usually a good thing to keep at the top level, not something to hide. The mistake to avoid is doing genuinely expensive, one-off work (like a large synchronous file read you only need for a single unusual request path) at the top level when it should only run when that specific code path is actually hit.
  3. Raise the memory setting on Init-heavy functions. Because CPU scales with memory, a function stuck at the 128 MB default can spend a surprising amount of Init time simply waiting for its own imports to finish loading. Testing a step up to 256 MB or 512 MB is a five-minute change with no code required.
  4. Pick a lighter framework for latency-sensitive functions. If you're on Java or .NET and reaching for a full dependency-injection framework out of habit, know that it's often the framework's bootstrapping — not your business logic — that's adding seconds to Init.
  5. Reuse SDK clients and connections instead of recreating them per request. An HTTP client, database connection, or AWS SDK client created inside the handler gets rebuilt on every single invocation, warm or cold. Creating it once outside the handler means only the cold start pays that price, and every warm invocation after it reuses the existing connection.

These five changes cost nothing beyond a redeploy, and for a lot of functions — especially anything in Python, Node.js, or Go with a reasonably small package — they're genuinely enough. If your cold starts are already under a couple hundred milliseconds, chasing them further with paid features is usually solving a problem your users can't feel.

Does switching to Arm (Graviton) help with cold starts?

One lever that doesn't get mentioned nearly often enough is processor architecture. Every Lambda function runs on either the traditional x86_64 architecture or Arm-based AWS Graviton2 processors, and the trade is specific: functions on Graviton2 are designed to deliver up to 19% better performance at 20% lower cost than the same function on x86 — adding up to as much as 34% better overall price-performance. That 20% lower duration rate applies whether the function is running on-demand or under Provisioned Concurrency.

Switching is close to a free experiment for most functions. If your code doesn't rely on architecture-specific compiled binaries — which describes the vast majority of functions written in interpreted languages like Python and Node.js, and most functions compiled to Java bytecode — you can change a function's architecture setting from x86_64 to arm64 without touching a line of code. And you can build, test, and deploy arm64 functions from an ordinary x86 development machine using AWS SAM and Docker Desktop, so you don't need Arm hardware sitting on your desk to try it.

✅ Why this is worth testing before anything else on this list

Switching architecture doesn't guarantee a faster cold start on its own, since the Init phase still has to download and unpack your code and run your setup logic regardless of processor. But the extra performance efficiency Graviton2 brings can shave real time off that work, and testing it costs nothing: publish a second version on arm64, compare its Init Duration against the x86 version in CloudWatch Logs, and roll back with a simple alias weight change if it doesn't help your specific function.

SnapStart: the low-cost option for supported runtimes

Lambda SnapStart works differently from the cheap fixes above — instead of making your Init phase faster, it lets you skip running it fresh at all. When you publish a version of a SnapStart-enabled function, Lambda runs your full Init phase once, then takes a snapshot of the memory and disk state of that already-initialized environment — using Firecracker microVM technology — encrypts it, and caches it. When a real invocation comes in and needs a new environment, Lambda resumes execution from that cached snapshot instead of starting your code from scratch, which is what gets you sub-second startup with little or no code change.

SnapStart isn't universal, though. It currently supports these Lambda managed runtimes and their corresponding base images, across both ZIP and container image deployment formats:

  • Java 11 and later
  • Python 3.12 and later
  • .NET 8 and later (if you use the Lambda Annotations framework for .NET, you need version 1.6.0 or later for compatibility)

Node.js and Ruby managed runtimes are not currently supported, and there are several limitations worth knowing before you flip the switch: SnapStart does not support Provisioned Concurrency on the same function, doesn't work with Amazon EFS or with ephemeral storage configured above 512 MB, and can only be used on published function versions and aliases pointing to a version — not on $LATEST.

⚠️ What SnapStart actually breaks if you skip a step

Because SnapStart resumes from a saved snapshot instead of running your Init code fresh every time, anything in your Init phase that's supposed to be unique per environment — a randomly generated ID, a fresh cryptographic key, a timestamp you assumed would reflect "now" — can come back identical across multiple resumed environments if you're not careful. This is a known uniqueness consideration, and there are runtime hooks specifically so you can regenerate that kind of state after a snapshot is restored, rather than trusting whatever was captured at snapshot time.

Turning SnapStart on

  1. In the Lambda console, open your function, go to Configuration, then General configuration, and choose Edit.
  2. Under SnapStart settings, choose Published versions, then Save.
  3. Publish a new version of the function. Lambda runs your Init phase once during that publish step, takes the snapshot, and caches it from that point forward.
  4. From the AWS CLI, the same change is one command: aws lambda update-function-configuration --snap-start ApplyOn=PublishedVersions.

SnapStart isn't limited to a handful of test regions anymore, either. Python and .NET support expanded to 23 additional AWS Regions in mid-2025, and support for container image functions arrived in 2026, so the old "SnapStart doesn't work for containers" caveat that circulated for years is now out of date for functions built on Java 11+, Python 3.12+, or .NET 8+ base images.

Provisioned Concurrency: the option that actually costs money

If SnapStart doesn't support your runtime, or your cold starts are still too slow for a genuinely latency-sensitive workload, Provisioned Concurrency is the next step up. The idea is simple: it's a number of pre-initialized execution environments you configure for a function ahead of time, kept ready and initialized so they can respond immediately — typically in double-digit milliseconds — effectively removing the cold start for traffic that falls within the amount you provisioned.

The catch is that "provisioned" means "reserved and paid for," whether it's actually handling a request at that exact moment or just sitting ready. The published rate is $0.0000041667 per GB-second, billed continuously for as long as it's enabled, on top of the (lower, provisioned-rate) duration charge for invocations that actually run against it. You're paying for standby capacity the same way you'd pay to keep the lights on in Jake's shop overnight so a customer could walk in at 3 AM — sometimes worth it, often not.

Reserved concurrency vs. provisioned concurrency

Lambda actually gives you two separate concurrency controls, and it's easy to mix them up. Reserved concurrency sets both a maximum and a minimum number of concurrent instances a function can use, reserving that capacity so no other function in the account can borrow it — and it's free to configure, with no additional charge. Provisioned concurrency is the one that costs money, because it doesn't just reserve a number, it keeps that many environments pre-initialized and idling, ready to respond immediately. And who actually benefits is clear: interactive workloads — the ones with a real person waiting on the other end, such as a web or mobile app — gain the most, while asynchronous workloads like data processing pipelines are usually not latency-sensitive enough to justify paying for standby capacity.

There's one more wrinkle worth knowing if you use both controls together: if the provisioned concurrency configured across a function's versions and aliases adds up to the function's full reserved concurrency, every invocation runs on provisioned concurrency, and that same configuration throttles the unpublished $LATEST version entirely, preventing it from executing. It's a deliberate safety mechanism, not a bug, but it's surprised more than one team mid-deploy.

Turning it on

  1. Open the function in the Lambda console and, from the Actions menu, choose Publish new version. Provisioned Concurrency can only be attached to a published version or an alias, never to $LATEST.
  2. On that published version or its alias, scroll to the Concurrency panel and choose Add configuration.
  3. Enter the number of concurrent environments you want kept pre-initialized and choose Save.
  4. Wait for it to finish preparing. The console shows a status of "In progress" while Lambda prepares the environments, then flips to "Ready" once they're actually standing by, typically within a couple of minutes; your function keeps serving traffic normally the entire time.

Letting it scale itself

Picking one fixed number and hoping it's right for every hour of the day is how teams either overpay for capacity nobody uses overnight, or undersize it and get cold starts anyway during a lunchtime spike. There's a way around that: register the function's alias as an Application Auto Scaling target with the RegisterScalableTarget API, then attach a target-tracking scaling policy with the PutScalingPolicy API. Once configured, Application Auto Scaling raises provisioned concurrency in large steps as the number of open requests increases, up to whatever maximum you set, and lowers it again as load falls — which tracks how real traffic actually behaves far better than a single number chosen once and forgotten.

You can watch whether any of this is actually working through two CloudWatch metrics: ProvisionedConcurrencyUtilization tells you how much of what you're paying for is genuinely being used, and ProvisionedConcurrencySpilloverInvocations tells you how many requests spilled over into a standard, on-demand cold start because your provisioned amount was already full at that moment.

Method Extra cost? Use it when
Trim package / reuse clients / raise memory / try arm64 No Always — do this first, on every function, regardless of anything else
Lambda SnapStart Small snapshot-related charge, far less than Provisioned Concurrency You're on Java, Python 3.12+, or .NET 8+ and want sub-second starts without paying for standby capacity
Provisioned Concurrency Yes — billed continuously while enabled Unsupported runtime for SnapStart, or you need guaranteed double-digit-millisecond response for a known, predictable amount of concurrent traffic
Scheduled "warming" pings Small (extra invocations) Rarely the right answer — only keeps one environment warm, so it doesn't help under real concurrent load

✅ Why this is the one to reach for last, not first

A function typically uses SnapStart or Provisioned Concurrency, not both at once, and Provisioned Concurrency cannot be applied to a function's $LATEST version — it only works against published versions and aliases. Most teams reach for Provisioned Concurrency before they've tried the free fixes, spend real monthly money, and then discover their cold start problem was actually a 40 MB deployment package the whole time.

How to actually see a cold start happening

You don't have to guess whether a slow request was a cold start. Every Lambda invocation writes a REPORT line to Amazon CloudWatch Logs, and when the invocation involved a cold start, that line includes an extra field called Init Duration — the time spent specifically on the Init phase, separate from your function's normal Duration. If a REPORT line has no Init Duration field, that invocation was a warm start.

  1. Open the function in the Lambda console and go to its Monitor tab, or open CloudWatch Logs Insights directly against the function's log group.
  2. Filter or query for log lines containing Init Duration to isolate cold-started invocations from the rest.

Reading Init Duration over time tells you two things a support ticket or a user complaint never will: how often your cold starts are actually occurring (versus your gut feeling), and whether a given fix — more memory, a leaner package, SnapStart — actually shortened them, rather than just feeling faster because you were watching more closely after making the change.

The edge cases nobody warns you about

Provisioned Concurrency doesn't mean "no cold starts, ever"

If your provisioned amount is 10 and traffic spikes so hard that Lambda needs an 11th concurrent environment, that 11th invocation gets a genuine cold start — and every one after it, until traffic falls back under 10 again or you raise the provisioned amount. This is one of the most common questions on AWS's community forum: someone enables Provisioned Concurrency, still sees cold starts in their metrics, and assumes the feature is broken. It's usually just undersized for the traffic pattern.

Background and async work often doesn't need any of this

If your function processes an SQS queue, reacts to an S3 upload, or runs a nightly batch job, nobody is staring at a spinner waiting on it. A cold start adding a second to a background job that runs unattended is not the same problem as a cold start adding a second to a page a customer is actively watching load. Spending money on Provisioned Concurrency for a function nobody is waiting on in real time is money spent solving a problem that doesn't exist for that workload.

Multiple functions chained together multiply the problem

If your API Gateway request triggers Function A, which invokes Function B, which calls Function C, and all three are cold at the same moment, your user experiences the sum of all three cold starts, not just one. This is common in overly fragmented microservice-style architectures and is a reason some teams deliberately consolidate several small functions into fewer, slightly larger ones for latency-sensitive paths.

Container image functions add their own wrinkle

Functions packaged as container images pull that image from Amazon ECR as part of getting the environment ready, and a larger image takes longer to pull. SnapStart extends to container images as well now, but you still get the fastest cold starts from a lean, purpose-built image rather than a general-purpose base image with everything installed.

When you've tried everything and it's still not fast enough

Here's the honest limit, the part most cold-start articles refuse to say out loud: sometimes Lambda is the wrong tool for a specific, extremely latency-sensitive path, and no amount of tuning changes that. If you've trimmed the package, raised the memory, enabled SnapStart where it's supported, sized Provisioned Concurrency to your real peak concurrency, and a user-facing request still occasionally eats a cold start it can't afford — a payment authorization screen, a live customer-facing chat — that's a real signal, not a tuning failure.

At that point the honest options are: accept the small residual risk (because even Provisioned Concurrency can be exceeded during an unpredictable spike), move that one specific latency-critical path to a persistently running compute option like an always-on container or a small EC2-backed service behind a load balancer, or redesign the user experience so the wait is invisible — showing a price estimate instantly while the precise number loads in the background, for example, is exactly the kind of change that would have saved Jake's demo. None of these are failures of Lambda. Lambda's whole design trades a small, occasional cold-start tax for not paying for idle servers the other 99% of the time, and for the vast majority of workloads, that trade is the right one.

A short order-of-operations, if you just want the sequence

  1. Confirm you actually have a cold-start problem by checking Init Duration in CloudWatch Logs — don't guess from a feeling.
  2. Trim the deployment package and move client/connection creation outside the handler, so it only runs once per environment.
  3. Test a higher memory setting; because CPU scales with memory, this alone often meaningfully shortens Init.
  4. Test the arm64 (Graviton2) architecture setting — same code for most functions, often faster and cheaper per GB-second.
  5. If you're on Java, Python 3.12+, or .NET 8+ and still need faster starts, enable SnapStart on a published version.
  6. If your runtime isn't supported by SnapStart, or your latency requirement is stricter than SnapStart delivers, size and enable Provisioned Concurrency to match your real peak concurrent traffic, ideally with Application Auto Scaling attached.
  7. Re-check Init Duration after each change, on real traffic, before moving to the next step.

Ethan's take on the order matters here, and he's not shy about it: "Everyone wants to jump straight to Provisioned Concurrency because it sounds like the 'proper' enterprise fix. It's usually the worst place to start, because it's the only one on this list that costs you money every single hour whether anyone's using the function or not. Fix the cheap stuff first. Most of the time, that's enough, and you never touch your wallet."

Frequently asked questions

What exactly triggers a Lambda cold start?

Five situations: the function's very first invocation, an invocation after enough idle time that Lambda recycled the environment, traffic scaling up beyond the currently warm environments, a fresh code or configuration deployment, and internal AWS load rebalancing that's outside your control.

How long does a cold start usually last?

AWS's analysis of production workloads puts the typical range from under 100 ms to just over 1 second, occurring in under 1% of invocations. Heavier runtimes like Java or .NET running large frameworks can run several seconds longer if they aren't optimized.

Do cold starts cost extra money now?

Yes. Since August 1, 2025, the Init phase duration is included in your billed duration for on-demand Lambda invocations across all runtime types and packaging formats. Before that date, this time was unbilled for ZIP-packaged functions on managed runtimes.

Is Provisioned Concurrency the same thing as SnapStart?

No. Provisioned Concurrency keeps a set number of environments permanently initialized and billed continuously, whether they're in use or not. SnapStart resumes new environments from a cached snapshot taken when you published the version, and is billed differently, at a lower rate than keeping standby capacity running around the clock. A function typically uses one or the other, not both.

Does putting my Lambda function in a VPC still cause slow cold starts?

Not the way it used to. AWS overhauled VPC networking for Lambda starting in 2019, replacing per-environment network interfaces with a shared Hyperplane ENI created once per function rather than once per cold start. The multi-second VPC cold-start penalty from before that change is no longer accurate advice.

Which programming language has the fastest Lambda cold start?

Lightweight interpreted or compiled runtimes like Python, Node.js, and Go typically cold-start faster than Java or .NET, largely because Java and .NET frameworks often do significant class loading and initialization work before your handler code ever runs. This isn't a hard rule for every function, but it's a consistent, well-documented pattern.

Does increasing memory actually reduce cold start time?

Often, yes. Lambda allocates CPU power in proportion to configured memory, reaching the equivalent of one full vCPU at 1,769 MB. A function stuck at a very low memory setting can spend extra Init time simply because it has less CPU available to load its own dependencies.

Can I keep a Lambda function warm with a scheduled ping?

You can, but it's a weak fix. A scheduled ping via EventBridge only keeps roughly one execution environment warm. If your real traffic needs several concurrent environments at once, the extras still cold-start the moment that traffic actually arrives, and you're paying for the ping invocations on top of it.

Why am I still seeing cold starts with Provisioned Concurrency enabled?

The most common reason is that your configured provisioned amount is lower than your actual peak concurrent traffic. Any invocation beyond the provisioned number gets a standard, on-demand cold start, exactly like a function with no Provisioned Concurrency at all.

Does deploying new code cause a cold start?

Yes, every time. Updating a function's code or its configuration retires the environments running the previous version, so the next invocation after any deployment is guaranteed to be a cold start.

Are cold starts actually a problem for background or async jobs?

Usually not in any way a person notices. If a function processes a queue or runs on a schedule with nobody watching a spinner, an extra second added by a cold start rarely matters. Cold starts become a real problem specifically on synchronous, user-facing request paths.

How do I see cold start time in CloudWatch Logs?

Look for the REPORT line Lambda writes for every invocation. When an invocation involved a cold start, that line includes an Init Duration field showing exactly how much time the Init phase took, separate from your normal function Duration.

Does SnapStart work for Node.js or Ruby functions?

Not currently. As of AWS's published documentation, SnapStart supports Java 11+, Python 3.12+, and .NET 8+ managed runtimes and their container-image equivalents. Node.js and Ruby managed runtimes, along with OS-only runtimes, are not supported.

Can a cold start actually fail or time out?

A cold start itself isn't a separate thing that fails independently — but if your Init-phase code (loading a huge dependency, waiting on a slow external connection) takes long enough, it can push your total invocation past your configured timeout, which does then fail. Long Init times are worth investigating for that reason alone, separate from user-perceived latency.

Is Lambda still worth using if cold starts bother me this much?

For the overwhelming majority of workloads, yes. Cold starts affect under 1% of invocations for typical production traffic according to AWS's own data, and most of that remaining friction is solvable with free changes to package size and memory before you ever need a paid feature. The cases where Lambda genuinely isn't the right fit — ultra-latency-critical synchronous paths with unpredictable spiky concurrency — are real, but they're the exception, not the norm.

Will AWS ever get rid of cold starts entirely?

There's no indication of that, and it's arguably not even the right goal. A true zero-cold-start world would mean AWS keeping compute idling for every customer's every function at all times, which contradicts the entire cost model that makes Lambda cheap in the first place. The realistic trajectory, based on features like SnapStart's steady rollout to more runtimes and regions, is AWS continuing to shrink cold starts and give you cheaper ways to avoid them for the functions that need it — not eliminating the concept altogether.

Where to Next..πŸ‘‡

πŸ’‘ Recommended AWS Foundations Reading (Optional)

Master the core building blocks of AWS infrastructure, networking, and security:

Complete series is in this link, even if you are non techie!

Revision note. Written September 2026.. It will need a revisit if AWS extends SnapStart to more runtimes or changes the INIT billing model again. If a cold start has ever cost you a customer the way it nearly did for Jake, you're not imagining the problem — it's real, it's documented, and it's fixable in roughly that order.

Related