Cold starts
A Socratic walk-through of cold starts — reasoned out one step at a time, not lectured.
The question we started with
THE QUESTION #Why is the first request to an idle service slow in a way the next thousand are not?
You call a function that has not been used for a while and it takes a second or two. You call it again and it answers in fifteen milliseconds. The request did not change, the code is identical, the network is the same. The only difference is that something ran recently.
That is a peculiar kind of slowness. It is not load-dependent — the system was completely idle, which usually means fast — and it is not random. It is a cost attaching to the first of a run and to no other member of it, which says a one-time expense is being paid and then amortised. Working out which expenses those are explains both why the effect exists and why most of the advice about it aims at the wrong half.
Reasoning it through
REASONING #Start with what must exist before your code can handle a request at all. A machine with capacity, which must be selected. An isolation boundary, which must be created. A language runtime, which must be started. Your code, which must be fetched from storage. Only then does your program begin.
Every one of those steps produces something reusable: the sandbox can serve many requests, the runtime once booted stays booted. So the platform keeps the assembly alive after the request finishes and routes the next one to it — and, since idle capacity costs money, reclaims it after a period of inactivity. That is the entire mechanism. There is no penalty for being idle; there is a setup cost that idleness caused to be discarded.
Now the part that people miss. Divide the setup into two halves and ask who controls each.
The platform's half is placement, sandbox creation, runtime boot, and code fetch, and its magnitude depends on the isolation technology. A V8 isolate — Cloudflare Workers' model — shares one process and one already-running runtime between many tenants, so starting one takes milliseconds. A microVM such as Firecracker, the basis of AWS Lambda, boots a stripped kernel in the low hundreds of milliseconds. A large container image that must be pulled is slower again. This half is the platform's engineering problem, and the half that has improved most.
Your half is initialisation: module imports, framework bootstrap, configuration parsing, SDK client construction, opening a connection, warming a cache, loading a model. Notice how little of this is proportional to your business logic — importing a large SDK to make one API call costs the same as importing it to make a thousand.
Which half dominates? On most real services, the second — and by a wide margin on runtimes with expensive startup semantics, where a heavyweight framework doing classpath scanning and reflective wiring can spend seconds before your handler's first line. That is why the effective remedies are unglamorous: import less, import lazily, defer anything the first request does not need, move work to build time. The platform half you mostly cannot touch; the half you can is usually the larger one.
So what do the platform features do? Provisioned concurrency keeps instances initialised so requests never meet a cold one — it does not make the start faster, it pays for it in advance, converting a latency problem into a bill. Snapshot approaches such as Lambda's SnapStart take a memory image after initialisation and restore from it, which is why they help most where initialisation is heaviest; the catch is that the image carries whatever was in memory when taken, so anything unique per instance — a random seed, a cached credential — must be regenerated on restore.
One last observation, because it reframes the problem. A cold start is not exceptional but the normal cost of elasticity: any system that scales to zero reconstructs itself on demand, and any system that scales up cold-starts every new instance precisely when it is busiest. The cold path is the traffic-surge path, and a service whose initialisation takes four seconds cannot absorb a spike however fast the platform allocates sandboxes.
The analogy
THE ANALOGY #It is a professional kitchen opening for service. The first order waits while the ovens come up to temperature, the stocks are brought out, the knives are set, the stations stocked. The two-hundredth order arrives into a kitchen where all of that already holds, and nothing about the dish changed. Notice which preparations are the slow ones: not the chef's technique, but the standing arrangements. A kitchen that opens fast is one that needs fewer of them.
a kitchen opens once and stays open, whereas a serverless platform tears each station down after minutes of quiet and opens dozens of new kitchens at once when a rush arrives — which is why the cold path matters most under load, not at the start of the day.
Clarifying the model
THE MODEL #Three refinements, and one measurement warning.
First, "cold start" names two things that get conflated. Platform setup time and your initialisation time are measured separately on most platforms — AWS reports an init duration distinct from invocation duration — and they respond to entirely different interventions. Any discussion that does not say which half it means is not actionable.
Second, keep-alive pings are a folk remedy with real limits. Keeping one instance warm does nothing for the fortieth instance a burst creates, and platforms recycle instances regardless of traffic. It reduces the symptom's frequency in low-traffic services and does not address the case that hurts most.
Third, the numbers move. Published cold-start figures are snapshots of a moving target. Trust the ordering — shared-runtime isolates faster than microVMs faster than large container images — and measure your own service rather than importing someone else's milliseconds.
The warning: cold starts live in the tail, so a mean hides them almost perfectly. A service where one request in two hundred takes two seconds looks healthy on averages and is visibly broken to the users who hit it. Watch the high percentiles, during scaling events rather than at rest.
A picture of it
THE PICTURE #How to readLeft to right is the life of one instance, not one request. Everything in the first section happens before your handler's first line and produces state that survives, which is why it is charged once and amortised over every later request. The split inside that band is the practical point: the first three entries belong to the platform, while the fourth is your own code and is usually the larger share. The last section is what makes the cost recur — reclaiming an idle instance discards all of it.
What became clearer
WHAT CLEARED #The first request is not slow because the system was resting; it is slow because it is paying, for everyone after it, for state that will then be reused until the platform reclaims it. Splitting that cost in two makes it tractable — one half is a property of the isolation technology you chose, the other is initialisation code you wrote and can usually shrink, and on most services the second is larger. And the case that matters is not the idle service at 3 a.m. but the traffic surge, where every new instance takes the cold path at the moment the system can least afford it.
Where to go next
ONWARD #- How snapshot restore interacts with anything that must be unique per instance, such as seeds and cached credentials.
- Whether provisioned concurrency is cheaper than the effort of shrinking initialisation.
Key terms
TERMS #| Term | What it means |
|---|---|
| Cold start | the added latency of a request that must first construct the execution environment and run initialisation. |
| Init duration | the separately reported portion of a cold start spent running your own initialisation code. |
| Isolate | a lightweight sandbox within a shared runtime process, as used by V8-based platforms. |
| MicroVM | a minimal virtual machine, such as Firecracker, isolating tenants at lower boot cost than a full VM. |
| Provisioned concurrency | keeping instances initialised in advance so requests do not meet the cold path, paid for continuously. |
| Snapshot restore | resuming from a memory image captured after initialisation, rather than re-running it. |
Every term the collection defines is gathered in the glossary.