Serverless cold starts: causes and fixes
The first request to an idle serverless function is slow because the platform has to start it. What happens during a cold start and six ways to shorten or avoid it.
A serverless function costs nothing while it is idle, because nothing is running. The price of that is the cold start: when a request arrives and no instance is ready, the platform has to create one first. The user who triggered it waits longer than everyone after them.
What happens during a cold start
- The platform prepares an execution environment.
- It downloads your code.
- It starts the language runtime.
- It runs your initialization code: imports, configuration, database clients.
- It runs the handler.
A warm request skips straight to step 5. You control steps 2 to 4, and that is where the fixes are.
1. Ship less code
A smaller package downloads and loads faster.
- Bundle the function and remove unused code.
- Leave out dev dependencies and test files.
- Import only the client you need from a large SDK, not the whole SDK.
2. Initialize once, and only what you need
Code outside the handler runs once per instance and is reused by every warm request. Put expensive setup there.
// Runs once per instance
const db = createClient(process.env.DATABASE_URL);
export async function handler(event) {
// Runs on every request and reuses the client
return db.query("select id, total from orders where user_id = $1", [event.userId]);
}
The other side of this rule: do not load at startup what only one rare code path uses. Import that inside the path.
3. Pick the runtime and memory deliberately
Node.js, Python and Go start quickly. Java and .NET start more slowly, although snapshot features such as AWS Lambda SnapStart narrow the gap.
Memory matters more than it seems. On AWS Lambda, CPU is allocated in proportion to memory, so a function with more memory often initializes faster. Sometimes it finishes so much sooner that the bill barely changes. Measure it instead of assuming the smallest setting is the cheapest.
4. Keep connections cheap
Opening a database connection with a TLS handshake on every cold start is slow, and a burst of new instances can exhaust the database's connection limit. Use a connection pooler or an HTTP based data API in front of the database.
5. Keep instances warm where it counts
For latency sensitive endpoints, pay for readiness: provisioned concurrency on AWS, or minimum instances on Google Cloud and Azure. These keep initialized instances waiting.
A scheduled ping every few minutes is the budget version. It keeps one instance warm. It does not help when traffic needs a second one.
6. Consider a lighter runtime
Edge platforms run code in lightweight isolates or as WebAssembly and start in milliseconds. For small, stateless handlers they remove the problem altogether.
Decide whether it matters
Check before optimizing. Your platform's logs report the initialization time of each cold start, and your latency percentiles show how many users feel it. A nightly batch job does not care about an extra second. A checkout API does. Spend the effort, and the money, where a person is waiting.