# How to reduce Vercel Function cold starts

> Confirm a Vercel cold start is really the cause, then cut it: fluid compute, smaller bundles, module-scope setup, the right region, memory and caching.

Part 12 of [Platform playbooks](https://polylane.com/series/platform-playbooks/)
By Boris Tane, Founder of Polylane · Published October 18, 2026 · 11 min read
Canonical: https://polylane.com/learn/troubleshooting/how-to-reduce-vercel-function-cold-starts/

First confirm that cold starts are the problem: call the endpoint three times with vercel httpstat and check the cold start percentage in Observability. If they are, turn on fluid compute for projects created before April 23, 2025, cut heavy dependencies, do expensive setup once at module scope, run the function in your database's region and cache responses with Cache-Control. Keep-warm cron jobs add little, because paid plans already pre-warm production and Hobby cron jobs run only once a day.

A cold start is the first invocation on a new Vercel Function instance. Vercel has to load your code and run its startup work before the handler can answer. Later requests that reach the same warm instance skip that cost. Fluid compute is the default for projects created since April 23, 2025, and it already reduces both how often cold starts happen and how much they cost. On a current project, most production requests reach an instance that is already running.

So the work goes in two steps. Prove that startup time is behind the latency you see. Then reduce it: smaller bundles, setup done once per instance, a region close to your data, more CPU for heavy startup work, and caching so fewer requests reach the function at all. The rest of this page covers each step, with the commands and config.

## What a Vercel cold start is and what it costs

A Vercel cold start is the delay before your handler runs on a fresh instance: the runtime loads, then parses and evaluates your code and its imports. That time grows with the amount of code involved. Vercel's own guidance says startup time tracks function size, and most of that size comes from external dependencies. Large packages can take seconds or more to parse and evaluate.

A public GitHub discussion on the Vercel repository shows the scale. One user had a GraphQL function with Prisma and Nexus, about 20 MB compressed and 63.5 MB uncompressed. They measured roughly 2 to 3 seconds of cold start delay, against about 500 ms for a warm request. A plain `/api/hello` route in the same project always answered in under 500 ms. In that case the bundle was the cost, and the platform was not.

There is one more source of slow first requests: **archived functions**. A production function that sits idle for two weeks gets archived. In preview the window is 48 hours. The first invocation after archiving adds at least one second of latency on top of a normal cold start.

## How fluid compute already cuts cold starts

Before you change any code, know what the platform does for you. Under fluid compute:

- **Shared instances.** Several invocations share one warm instance, so a new request reuses a running instance when there is room. Vercel calls this optimised concurrency, and it applies to the Node.js and Python runtimes.
- **Bytecode caching.** On Node.js 20 and later, Vercel stores the compiled bytecode after first execution, so later cold starts skip recompiling. It applies to production deployments only, never to development or preview.
- **Pre-warmed instances.** On paid plans, production deployments keep instances warm and ready.
- **Automatic concurrency scaling.** Vercel adds capacity ahead of demand, up to 30,000 concurrency on Hobby and Pro and 100,000 or more on Enterprise.

If your project predates April 23, 2025, it may not have any of this. That is the cheapest fix on the page.

## Confirming the slow request is a cold start

Slow responses often come from the work the function does: a slow query, a third-party API, a database in another region. Startup tuning does nothing for those. Run these checks first. Link the project with `vercel link` if you have not already.

```mermaid alt="Decision flow from a slow Vercel Function response to the cold start fix that applies"
graph TB
A[Slow Vercel Function response] --> B{First call slow, next two fast?}
B -->|no| C[Fix in-function work and API calls]
B -->|yes| D{Fluid compute on?}
D -->|no| E[Enable fluid compute and redeploy]
D -->|yes| F{Idle for long stretches?}
F -->|yes| G[Cache responses or rely on pre-warming]
F -->|no| H[Shrink bundle and move setup to module]
E --> I[Measure again on production]
G --> I
H --> I
style C fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d
style E fill:#d1fae5,stroke:#6ee7b7,color:#065f46
style H fill:#d1fae5,stroke:#6ee7b7,color:#065f46
```

1. **Time three calls in a row.** `vercel httpstat` splits a request into DNS, TCP, TLS, server processing and content transfer. Server processing is the time spent inside your function.

```bash
vercel httpstat /api/slow-endpoint   # first call, possibly cold
vercel httpstat /api/slow-endpoint   # second call, warm
vercel httpstat /api/slow-endpoint   # third call, warm
```

If only the first call is much slower, startup is adding latency. If all three are slow, look at the code and its dependencies.

2. **Check the Observability tab.** Vercel exposes three numbers worth reading here: function startup performance (how long functions take to become ready), the percentage of invocations that were cold starts, and latency to external APIs. A low cold start share means the users who complain are mostly hitting something else.

3. **Sort the External APIs tab by P75.** If one upstream dominates, fix that first. Startup work will not move it.

4. **Check the configuration the deployment runs with.** `vercel inspect` shows memory, region and max duration.

```bash
vercel inspect <deployment-url>
```

5. **Log cold starts from your own code.** A module-scope flag tells you whether an invocation was the first on its instance, and how long ago the module loaded. When I built and led the Workers observability team at Cloudflare, this split between startup and handler time was the first thing worth seeing on any serverless platform. Without it, a slow cold request and a slow warm request look the same in the logs.

```ts
// app/api/orders/route.ts
const bootedAt = Date.now();
let firstOnInstance = true;

export async function GET() {
  const cold = firstOnInstance;
  firstOnInstance = false;
  const started = Date.now();
  const data = await loadOrders();
  console.log(JSON.stringify({
    route: '/api/orders',
    cold,
    msSinceBoot: started - bootedAt,
    handlerMs: Date.now() - started,
  }));
  return Response.json(data);
}
```

Filter for it in the logs:

```bash
vercel logs --environment production --source serverless --query "cold" --since 1h --expand
```

## Fixes for Vercel cold starts, in the order they pay off

### Turn on fluid compute for projects older than April 2025

Open **Settings**, then **Functions**, find **Fluid Compute**, switch it on and save. Then deploy, because the change only applies to new deployments. To try it on one environment or deployment first, set it in `vercel.json`:

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "fluid": true
}
```

This gives older projects shared instances, bytecode caching, pre-warmed instances and automatic concurrency scaling in one change.

### Shrink what the function loads at startup

Every package your function imports gets parsed and evaluated on a cold start. Run a bundle analyser over your build, find the heaviest packages, and remove or replace what you can. Look for packages that only belong in dev or build tooling. In the GitHub discussion above, `prettier` parsers had ended up in a production function bundle.

Three more changes help:

- **Upgrade Next.js.** Vercel says recent versions include startup improvements, especially with external packages such as Sentry. In the same discussion, Vercel's answer was to run the latest Prisma and Next.js, as both had shipped cold start work.
- **Bundle externals on the Pages Router.** Set `bundlePagesExternals` under the experimental flag:

```js
// next.config.js
module.exports = {
  experimental: {
    bundlePagesExternals: true,
  },
};
```

- **Load rare code paths on demand.** If one route has a branch that pulls in a large library, a dynamic import keeps it out of startup:

```ts
export async function POST(request: Request) {
  const body = await request.json();
  if (body.format === 'pdf') {
    const { renderPdf } = await import('../../lib/pdf');
    return new Response(await renderPdf(body), {
      headers: { 'Content-Type': 'application/pdf' },
    });
  }
  return Response.json({ ok: true });
}
```

Vercel describes this as a fix for the uncommon case of divergent code paths. Use it where one heavy branch serves few requests.

### Do expensive setup once per instance

Work inside the handler runs on every request. Work at module scope runs once per instance, and under fluid compute that instance serves many invocations at once. Create database clients, SDK clients and parsed config at module scope:

```ts
// lib/db.ts
import { PrismaClient } from '@prisma/client';

// Created once when the instance starts, reused by every invocation on it
export const db = new PrismaClient();
```

```ts
// app/api/orders/route.ts
import { db } from '../../../lib/db';

export async function GET() {
  const orders = await db.order.findMany({ take: 20 });
  return Response.json(orders);
}
```

This does not remove the startup cost. It makes sure you pay it once per instance, and never again on warm requests.

### Put the function next to its data

Functions run in `iad1` (Washington, D.C.) by default. If your database is in Europe, every query on the cold path pays the round trip, and so does connection setup. Set the default region in **Settings**, then **Functions**, then **Function Regions**, or in `vercel.json`:

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "regions": ["cdg1"]
}
```

You can override it for one function through the `functions` property, which helps when different routes read from databases in different places. You can also deploy with `vercel --regions cdg1`. Hobby gets a single region. Pick only regions close to the services your functions call.

### Give CPU-heavy startup more memory

Memory sets CPU. The Standard size is 2 GB with 1 vCPU, and Performance is 4 GB with 2 vCPUs. Parsing a big bundle uses CPU, so more vCPU can shorten startup. On Pro and Enterprise, change it under **Settings**, **Functions**, **Advanced Settings**, **Function CPU**, then redeploy. You cannot set memory in `vercel.json`: Vercel gives a build-time warning if you try. Hobby always runs at 2 GB and 1 vCPU.

More memory raises the Provisioned Memory rate while requests are in progress. It can also lower Active CPU time if the work finishes faster. Measure before and after.

### Serve repeat requests from the cache

A cached response never reaches the function, so it can never be cold. For responses that tolerate being a little stale, set `Cache-Control`. Vercel's cache supports `stale-while-revalidate`:

```ts
export async function GET() {
  const data = await getCatalogue();
  return Response.json(data, {
    headers: {
      'Cache-Control': 's-maxage=60, stale-while-revalidate=300',
    },
  });
}
```

Cache misses still go to your origin, so the first request after expiry pays full cost.

## Keep-warm cron jobs on Vercel: when they still help

The old trick is to ping a route on a schedule so an instance never goes idle. Weigh three facts first:

- **Hobby can't schedule it often enough.** Hobby cron jobs run at most once per day, with timing precision of an hour. An expression like `*/5 * * * *` fails the deployment. Pro and Enterprise allow once per minute.
- **Paid plans already pre-warm production.** Fluid compute keeps instances warm on paid plans, so a cron ping mostly duplicates what you already get.
- **A ping warms one instance.** A burst of traffic that needs new instances still pays the startup cost. In the GitHub discussion, the user who pinged every 3 minutes reported it helped in about 80% of cases.

Cron invocations are function invocations and are billed as such. Where a ping still makes sense is a rarely used production route on Pro where a daily or hourly ping stops the function from reaching the two-week archive window. A Pro configuration:

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "crons": [
    { "path": "/api/keep-warm", "schedule": "0 * * * *" }
  ]
}
```

Make the target route import the same heavy modules as the routes you care about, and return straight away. A ping to a tiny route warms an instance of the tiny route.

## What goes wrong when you chase Vercel cold starts

- **Measuring on a preview and seeing no gain.** Bytecode caching only runs in production. Pre-warming applies to production deployments too. A preview will show worse cold starts than production will. Use previews to check correctness, and judge startup numbers on production after `vercel deploy --prod`.
- **Blaming cold starts for a slow upstream.** If all three `httpstat` calls are slow, startup is not the issue. Check the External APIs tab.
- **Testing a preview after two quiet days.** Preview functions archive after 48 hours idle, which adds at least a second to the first call. That number says nothing about production.
- **Chained functions stacking their cold starts.** In the GitHub discussion, a 7 second response came from a function that called other endpoints that were also cold. Call shared logic as a module import, and avoid HTTP calls between your own functions on the request path.
- **Setting memory in `vercel.json`.** It is ignored with a warning. Use the dashboard, and check with `vercel inspect` after deploying.
- **Creating clients inside the handler.** Each request then rebuilds what a module-scope client would have kept, and you pay a startup-sized cost on warm requests.
- **Assuming old forum numbers apply.** Many posts predate fluid compute, bytecode caching and pre-warming. Measure your own project before you plan around them.

## Proving the fix on production

Run the same measurement before and after each change, one change at a time:

```bash
vercel deploy                       # preview: check the route still works
vercel curl /api/slow-endpoint --deployment <preview-url>
vercel deploy --prod
vercel httpstat /api/slow-endpoint  # repeat three times
vercel logs --environment production --source serverless --since 5m
```

Then go back to Observability. The cold start percentage and startup performance for the route should drop, and the external API latency tells you what is left to fix once startup is no longer the largest cost.

## Watching Vercel startup latency with Polylane

Polylane detects production issues and fixes them by opening pull requests. It connects to Vercel, so when a route's startup latency climbs, it works out why, and the fix arrives ready for your team's normal review.

Running on Vercel? See [how Polylane monitors Vercel in production](https://polylane.com/for/vercel/).

## Common questions

**How do I know if a slow Vercel Function is a cold start?**

Run vercel httpstat against the endpoint three times in a row. If the first call is much slower and the next two are fast, startup time is the cause. Then check the cold start percentage and function startup performance in the Observability tab to see how much of your traffic it affects.

**Does fluid compute remove cold starts on Vercel?**

It reduces them. It lets many invocations share one warm instance, caches bytecode on Node.js 20 and later in production, pre-warms production instances on paid plans and scales capacity ahead of demand. A new or archived instance still has a startup cost.

**Can I keep a Vercel Function warm with a cron job on Hobby?**

Not often enough to matter. Hobby cron jobs run at most once per day, and an expression such as every 5 minutes fails at deployment. Pro and Enterprise allow once per minute, but paid plans already pre-warm production instances.

**Why is the first request after a long idle period even slower?**

Vercel archives a function after two weeks idle in production and 48 hours in preview. The first invocation after that adds at least one second of latency. Regular production traffic, or caching responses, keeps users from hitting it.

**Does more memory reduce Vercel cold starts?**

It can, because memory sets CPU: 2 GB gets 1 vCPU and 4 GB gets 2 vCPUs, and parsing a large bundle is CPU work. Only Pro and Enterprise can change it, in Settings, Functions, Advanced Settings. Memory can't be set in vercel.json.

**Why are cold starts worse on my preview deployment than in production?**

Bytecode caching applies only to production deployments, and pre-warmed instances are for production too. Preview functions also archive after 48 hours idle. Judge startup improvements on a production deployment.

**Which Next.js settings help with cold starts on Vercel?**

Upgrade to a recent Next.js, which Vercel says includes startup improvements, especially with packages like Sentry. On the Pages Router, set bundlePagesExternals to true under experimental in next.config.js. Use dynamic imports for heavy code that only some requests need.

## Sources

- [How can I improve function cold start performance on Vercel?](https://vercel.com/kb/guide/improve-function-cold-start-performance-on-vercel)
- [Fluid compute](https://vercel.com/docs/fluid-compute)
- [Debugging slow Vercel Functions](https://vercel.com/docs/functions/debug-slow-functions)
- [Configuring Memory and CPU for Vercel Functions](https://vercel.com/docs/functions/configuring-functions/memory)
- [Configuring regions for Vercel Functions](https://vercel.com/docs/functions/configuring-functions/region)
- [Usage and Pricing for Cron Jobs](https://vercel.com/docs/cron-jobs/usage-and-pricing)
- [Experiencing long lambda cold start delays of 2 - 3 seconds on Vercel (GitHub discussion)](https://github.com/vercel/vercel/discussions/7961)
- [Polylane documentation](https://docs.polylane.com/llms-full.txt)
- [Polylane full content](https://polylane.com/llms-full.txt)

## About the author

Boris Tane is the founder of Polylane. He previously founded Baselime, observability for the future of the cloud, which Cloudflare acquired. At Cloudflare he built and led the Workers observability team.

## More in this series

[Platform playbooks](https://polylane.com/series/platform-playbooks/)

1. [How to Monitor a Django App on Render](https://polylane.com/learn/monitoring-coverage/how-to-monitor-a-django-app-on-render/)
2. [How to Monitor a FastAPI App on Railway: Logs, Traces and Alerts](https://polylane.com/learn/monitoring-coverage/how-to-monitor-a-fastapi-app-on-railway/)
3. [How to Monitor a Supabase App in Production](https://polylane.com/learn/monitoring-coverage/how-to-monitor-a-supabase-app-in-production/)
4. [How to Debug Cloudflare Workers Errors: Logs, Traces and Error Codes](https://polylane.com/learn/troubleshooting/how-to-debug-cloudflare-workers-errors/)
5. [How to debug Vercel function timeouts](https://polylane.com/learn/troubleshooting/how-to-debug-vercel-function-timeouts/)
6. [Vercel 504 Gateway Timeout on Serverless Functions: Causes and Fixes](https://polylane.com/learn/troubleshooting/vercel-504-gateway-timeout-on-serverless-functions/)
7. [Cloudflare Workers error 1101: causes and how to fix it](https://polylane.com/learn/troubleshooting/cloudflare-workers-error-1101/)
8. [Cloudflare Hyperdrive connection errors: causes and fixes](https://polylane.com/learn/troubleshooting/cloudflare-hyperdrive-connection-errors/)
9. [How to Monitor a Convex App in Production](https://polylane.com/learn/monitoring-coverage/how-to-monitor-a-convex-app-in-production/)
10. [How to roll back a Cloudflare Workers deployment](https://polylane.com/learn/deployment-safety/how-to-roll-back-a-cloudflare-workers-deployment/)
11. [How to Monitor Vercel Cron Jobs: Heartbeats, Logs and Alerts](https://polylane.com/learn/monitoring-coverage/how-to-monitor-vercel-cron-jobs/)
12. [How to reduce Vercel Function cold starts](https://polylane.com/learn/troubleshooting/how-to-reduce-vercel-function-cold-starts/)
13. [How to Monitor a Next.js App on Render](https://polylane.com/learn/monitoring-coverage/how-to-monitor-a-next-js-app-on-render/)
14. [Modal container out of memory: find the cause and fix it](https://polylane.com/learn/troubleshooting/modal-container-out-of-memory/)
15. [Fix Railway “Healthcheck failed!” on deploy](https://polylane.com/learn/troubleshooting/railway-healthcheck-failed/)

Previous: [How to Monitor Vercel Cron Jobs: Heartbeats, Logs and Alerts](https://polylane.com/learn/monitoring-coverage/how-to-monitor-vercel-cron-jobs/)
Next: [How to Monitor a Next.js App on Render](https://polylane.com/learn/monitoring-coverage/how-to-monitor-a-next-js-app-on-render/)

## Related

- [Fix Vercel “FUNCTION_INVOCATION_FAILED” 500 errors](https://polylane.com/learn/troubleshooting/vercel-function-invocation-failed/): Why a Vercel Function returns 500 FUNCTION_INVOCATION_FAILED, how to find the crash in runtime logs, and how to fix throws, missing env vars, files and memory.
- [Fix “TypeError: fetch failed” on Vercel and Next.js](https://polylane.com/learn/troubleshooting/vercel-typeerror-fetch-failed/): Why Node.js throws TypeError: fetch failed on Vercel, how to read err.cause to find the real error, and how to fix localhost fetches, bad URLs and timeouts.
- [Fix Prisma “P1001: Can't reach database server”](https://polylane.com/learn/troubleshooting/prisma-p1001-cant-reach-database-server/): Why Prisma prints “P1001: Can't reach database server”, how to test the host and port it names, and how to fix private hosts, IPv6, paused databases and URLs.

Get started with one command: `curl -fsSL https://polylane.com/setup | bash` installs the CLI, connects your coding agents, and creates the account.
