Development

Edge Function Cold Starts: Causes and How to Reduce Them

Antonio Evans8 min read

A split diagram: on the left, a cold start timeline with four stages (provision, init, load, handle) shown as stacked blocks; on the right, a warm start timeline with only the handle block.
Key points

Key points

  1. 1A cold start at the edge is the delay before your code runs: the platform must place an isolate, initialize the runtime, load and evaluate your bundle, then execute your first request.
  2. 2Edge runtimes shrink the problem rather than remove it, because V8 isolates start far faster than containers but can still be evicted, scaled out, or replaced after a deploy.
  3. 3Most avoidable cold-start time lives in your own bundle and top-level initialization, not in the platform's provisioning step.
  4. 4Measure cold versus warm latency separately, then fix the largest contributor: bundle size, initialization work, instance reuse, or network path.

What a cold start actually is at the edge

A cold start is any first request served by an execution environment that has not yet handled traffic. At the edge it has four phases that stack up:

  1. Scheduling and placement: the request arrives at a point of presence and the platform decides which machine and context runs your code.
  2. Runtime initialization: the engine and platform bindings are set up, whether a V8 isolate, a JS runtime such as Deno, or a container.
  3. Code load and module evaluation: your bundle is transferred, parsed, and every imported module runs its top-level code before your handler is reached.
  4. First-request work: route matching, auth checks, cache miss, and downstream fetches that your handler performs on that first call.

A useful definition restates the same idea: cold starts occur on first invocation, when scaling from zero, when the provider reallocates resources, and when an instance expires, and the symptom is added latency, reduced throughput, and variance PayPro Global.

Edge functions are serverless functions moved to the nearest network node, which cuts distance but does not change the initialization model Netlify. The platform still needs a fresh execution context somewhere.

How edge cold starts differ from origin cold starts

An origin cold start is a data center problem: a new container or virtual machine boots, pulls its image, starts a full language runtime, then connects to the database. A regional serverless function averages roughly 50 to 500 milliseconds of cold-start boot time, versus 50 to 200 milliseconds at the edge in the same comparison Netlify. Other practitioner write-ups put Node.js containers at 50 to 200 milliseconds and over a few megabytes of RAM, and a fresh V8 isolate at under a millisecond and a few hundred kilobytes ResumeLens.

The key architectural difference: an edge runtime uses one JavaScript engine process per machine, shared by many lightweight isolates, instead of a new operating-system process per function Cloudflare. That model removes virtual-machine cold starts, but isolates are explicitly not long-lived. Cloudflare lists resource limits on the machine, suspicious behavior, and per-isolate resource limits as reasons an isolate may be spun down or evicted, and advises against storing mutable state in global scope Cloudflare.

DimensionOrigin or regional serverlessEdge on V8 isolates
Unit created per burstContainer or VM plus full runtimeIsolate inside an existing engine process
Typical cold-start contribution50 to 500 ms, sometimes moreSub-10 ms to low hundreds of ms, workload dependent
Memory per instanceMegabytesHundreds of kilobytes
Reuse modelWarm container kept for idle windowIsolate reused until evicted
Main user-side costPackage size, runtime, VPC setupBundle size, module evaluation, top-level work
Failure modeSlow first request after idleSlow first request after deploy, spike, or eviction

Sources for the row values: Netlify, ResumeLens, Cloudflare.

The practical consequence: at the edge you rarely budget for a full process boot, but you still budget for parsing and evaluating every module you ship.

Why edge runtimes still pay a startup cost

Three mechanisms explain almost every edge cold start you will see.

Isolate reuse is best-effort, not guaranteed

Any two requests may land on the same instance or different ones, because placement depends on load and routing Cloudflare. A global footprint widens the chance that a request in a quieter region arrives at a machine that has not run your code lately. Popular function in a busy colo: warm. Same function at 3 a.m. in a small market: cold.

Deploys and scaling reset warmth

Every deployment creates new versions to place. First invocation after a deployment is a cold start, as is scaling from zero after inactivity and reallocation to new hosts PayPro Global. A canary or regional rollout multiplies the number of first requests.

Your own bundle is evaluated before your handler

This is the part teams underestimate. The runtime loads the bundle, parses it, and evaluates every module before handling a request, so bundle size and module graph depth directly set startup work Valibot. In one measurement of two worker builds with an identical schema, a class-based validation library produced 141.43 KiB raw and 26.75 KiB gzipped, while a modular library produced 11.92 KiB raw and 2.72 KiB gzipped, a 9.8x difference in gzipped size Valibot. That does not prove a 9.8x cold-start change; it isolates the code the runtime must process at startup.

Academic work confirms the direction of travel. A systematic review of serverless cold-start research groups mitigations into caching and application-level optimization, plus machine-learning approaches, and notes the problem remains open ACM. A separate study of prewarming containers in serverless edge environments reports up to 60 percent fewer cold starts and 45 percent better average response times than prior methods, by predicting requests and adjusting container state IEEE. You will not control the scheduler, but the application-level half of the mitigation is yours.

Worked example, using the published figures as illustrative inputs rather than a benchmark: a regional Node.js function averages 300 ms cold start. The same logic at the edge lands nearer 120 ms because isolate startup is cheaper Netlify. If your route is called 5,000 times an hour across 30 points of presence, you might see one cold request per node per hour in steady state, about 30 cold requests, so the blended extra latency is small. Now add a deploy every 20 minutes: every rollout re-colds each node, and the blend worsens without your code changing at all.

How to measure cold starts properly

You cannot optimize what you bundle into one latency number. Separate cold from warm, and separate startup from your own handler work.

Log the phases inside your function

Record a timestamp at module scope, one at the start of the handler, and one before you return. The gap proves whether time went into evaluation or into your handler.

const bootedAt = performance.now();
let requestCount = 0;

export default {
  async fetch(request: Request): Promise<Response> {
    const handlerStart = performance.now();
    const isLikelyCold = requestCount++ === 0;
    const msSinceBoot = Math.round(handlerStart - bootedAt);

    const res = await handle(request);

    const headers = new Headers(res.headers);
    headers.set("server-timing", `boot;dur=${msSinceBot ?? msSinceBoot},handler;dur=${Math.round(performance.now() - handlerStart)}`);
    headers.set("x-instance-req", String(requestCount));
    return new Response(res.body, { status: res.status, headers });
  },
};

async function handle(request: Request): Promise<Response> {
  return new Response("ok");
}

x-instance-req equal to 1 says this isolate had not served before. Compare the server-timing boot value between req-1 responses and later ones. The difference is your initialization cost, isolated from network time.

Measure from outside with warm and cold probes

From several regions, sample your endpoint once after a quiet period (cold) and ten times in quick succession (warm). Keep a table:

ProbeRequestsExpected signalWhat it tells you
Cold probe1 per region after idleHigh p95, instance count 1Startup plus network
Warm burst10 back to backLow, stableHandler and downstream cost
Post-deploy probe1 per region after releaseSpikes againDeploy-induced recold
Bundle probe1 small extra routeLow startupBaseline platform cost

If the warm burst is also slow, you have a downstream or network problem, not a cold start. A public report of poor edge function performance showed roughly 1.2 s cold starts and 200 to 400 ms on repeated requests, with a maintainer responding that internal medians were 400 ms cold and 125 ms warm, varying by region Supabase discussion. That gap is exactly why you measure both numbers yourself instead of trusting one vendor figure.

Watch out for the region mismatch

An edge function running nearest the user but calling a database in another region pays round trips that swamp startup. One user report described a trivial function at about 100 ms from any region, but a database-backed function rising from 250 ms in one region to 1.1 s and 2.3 s in others Supabase discussion. Before blaming the runtime, check where your data lives, and choose a runtime or region setting that can co-locate compute with the datastore. Provider documentation spells out the trade-off between user proximity and origin proximity IBM Cloud.

Reducing cold starts: seven tactics that work

1. Shrink the bundle first

Tree-shake, import individual functions instead of whole libraries, and prefer platform APIs over utility packages. The measured 9.8x gzipped difference between two validation libraries for one schema shows how much design choice matters Valibot. A practitioner account of trimming a serverless package from 40 MB to under 5 MB reports cold starts dropping by over 400 ms Medium, Ahmedrao. Edge bundle limits force the discipline: one guide cites 1 MB compressed on a free plan and 10 MB on a paid plan for a Workers-style runtime, and 1 MB for another edge platform ResumeLens.

2. Move heavy work out of module scope

Anything at top level runs on every cold start even when that request does not need it. Lazy-load the PDF generator, the email client, the big parser, inside the branch that uses it.

export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    if (url.pathname === "/report") {
      const { renderPdf } = await import("./pdf.js");
      return renderPdf(request);
    }
    return new Response("ok");
  },
};

Guidance for a Deno-based edge runtime is the same: avoid top-level await for slow operations and keep bundles small, with functions under 5 MB and minimal imports typically cold-starting under 200 ms RapidDev.

3. Cache connections and computed config in the isolate

Isolates persist between requests until evicted, so a lazily initialized client pays off across a warm window, but never assume it survives Cloudflare. Use a memoized getter, and treat any cached state as disposable.

4. Warm critical routes with scheduled pings

A low-frequency request per region keeps a route hot during predictable traffic, at the cost of billed invocations. Deno-based functions can use a scheduled database job to ping routes RapidDev, and scheduled invocations are a standard mitigation in container-based serverless PayPro Global. Keep the ping route tiny so the warm request itself is cheap.

5. Choose the runtime that matches the job

If you need a filesystem, native modules, or long execution, a Node.js runtime at a region near your data beats a cold-start-optimized edge runtime that cannot run your code. Edge runtimes support web APIs only, with no filesystem, no native modules, and tightly capped execution time OneUptime. Code that reaches for Node crypto, buffers, or dynamic evaluation fails there outright, and a runtime switch is the honest fix dev.to.

6. Deploy in a way that limits recold

Batch releases, pre-warm the new version from a synthetic client before shifting traffic, and keep a small health route to hit. Every deploy creates first invocations PayPro Global, so fewer, larger releases beat many small ones.

7. Co-locate compute with data, or cache the result

If the handler cannot avoid a distant datastore, cache the response at the edge and treat the cold path as the slow path. Infrastructure context on how edge and origin servers interact, and why traffic routes to the nearest node, is worth reading before you redesign Stack Overflow, Imperva.

Decision rule: if your p95 cold start is under roughly 100 ms and warm latency dominates, stop optimizing startup and fix the downstream. If cold adds more than 200 ms over warm, work through bundle size, top-level work, and warming in that order, because those are the levers you control.

Where Quanta fits

When an edge route needs to call a language model as part of the first request, that call can dominate the cold path. Routing text, image, audio, and embedding models through one OpenAI-compatible endpoint removes per-provider clients and SDK weight from the bundle. Quanta Inference is the natural next step when the model call is the slow part of your handler.

FAQ

FAQ

Does moving to the edge remove cold starts?

No. It makes the unit that starts much cheaper, replacing containers with isolates that launch in a fraction of the time, but isolates are still created after idle periods, evictions, and deploys Cloudflare. You reduce the cost per start, not the existence of starts.

Why is my function slow on the first request after a deployment?

New versions must be placed on machines that have not run them. The first invocation per location is a cold start by definition PayPro Global. Warm the new version before shifting full traffic, and keep the bundle small so each first request is cheap.

Is a big dependency really a cold-start problem if it is tree-shaken?

Only if the bundler can actually drop the unused parts. A class-based library with tightly coupled internals can pull its parsing engine and error handling in regardless of how little you use, which is how one schema produced a gzipped bundle 9.8x larger than a modular alternative Valibot. Check the built artifact, not the source import.

Should I use provisioned capacity or warming pings?

Use warming pings for predictable, low-volume critical routes where a small number of extra billed requests is acceptable RapidDev. Provisioned concurrency keeps instances ready on container-based platforms at higher cost PayPro Global. Neither fixes a bloated bundle, so shrink first.

Why does my edge function feel slow only for some users?

Likely a long trip to your data, not startup. An edge function near the user that queries a database on another continent pays round trips that dwarf isolate startup Supabase discussion. Compare cold and warm latency per region before changing the runtime.

Sources

Sources

  1. What is Cold Start? Understanding Serverless Latency - PayPro Global
  2. Edge Functions Explained | Netlify Blog
  3. Edge Runtime: Vercel Edge Functions, Cloudflare Workers, Deno Deploy
  4. How Workers works - Cloudflare Docs
  5. How Dependency Size Impacts Cold Starts in Edge ...
  6. Cold Start Latency in Serverless Computing: A Systematic Review ...
  7. Dynamic Pre-Warm Strategies for Reducing Cold-Start Latency in ...
  8. Poor performance with Edge Functions #29301
  9. Is it possible to choose which region(s) the Edge functions run in?
  10. How Edge functions work | IBM Cloud Docs
  11. Reducing Node.js Cold Starts in AWS Lambda
  12. How to Reduce Cold Start Time in Supabase Edge Functions
  13. How to Fix 'Edge Runtime' Limitations in Next.js - OneUptime
  14. Edge Runtime vs Node.js Runtime: When Your Serverless Functions ...
  15. Exploring the infrastructure and code behind modern edge functions
  16. What is an Origin Server | Origin vs Edge Server | CDN Guide - Imperva
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles