- name
- vercel-functions
- description
- Vercel Functions expert guidance — Node.js/Bun/Python runtimes, Fluid Compute, long-duration (30 min) functions, large functions (5 GB bundles), Docker/OCI container images, plan limits, streaming, WebSockets, and Cron Jobs. Use when configuring, debugging, or optimizing server-side code running on Vercel.
- metadata
- {"priority":8,"docs":["https://vercel.com/docs/functions","https://vercel.com/docs/functions/runtimes","https://vercel.com/docs/functions/limitations","https://vercel.com/docs/functions/configuring-functions/duration","https://vercel.com/docs/functions/container-images","https://vercel.com/docs/fluid-compute","https://vercel.com/docs/functions/websockets"],"sitemap":"https://vercel.com/sitemap.xml","pathPatterns":["api/**/*.*","pages/api/**","src/pages/api/**","app/**/route.*","src/app/**/route.*","apps/*/api/**/*.*","apps/*/app/**/route.*","apps/*/src/app/**/route.*","apps/*/pages/api/**","vercel.json","apps/*/vercel.json","vercel.ts","apps/*/vercel.ts","Dockerfile.vercel","*/Dockerfile.vercel","Containerfile.vercel","*/Containerfile.vercel"],"bashPatterns":["\\bvercel\\s+dev\\b","\\bvercel\\s+logs\\b","\\bvercel\\s+vcr\\b"],"importPatterns":["ws","socket.io","socket.io-client"],"promptSignals":{"phrases":["websocket","websockets","web socket","socket.io","long polling","long-polling","maxduration","max duration","function timeout","function times out","long-running function","long running function","504","unzipped maximum size","250mb","250 mb","large function","bundle size limit","dockerfile","container image","container registry","runtime edge","edge runtime"],"allOf":["[Truncated]","[Truncated]"],"anyOf":["realtime","bidirectional","ws server","polling","server-sent events","fluid compute","active cpu"],"noneOf":[],"minScore":6}}
# Vercel Functions
You are an expert in Vercel Functions — the compute layer of the Vercel platform.
## Prefer Node.js over the Edge runtime
**Strongly prefer Node.js. Don't reach for `runtime = 'edge'` in new code, and recommend migrating off it when you find it in existing code.**
This follows Vercel's own documented guidance — *"we recommend migrating from edge to Node.js for improved performance and reliability"* — and one hard constraint: as of **Next.js 16.3, `runtime = 'edge'` is no longer supported**. Routes and pages there run on Node.js regardless of what you write, so on 16.3+ this stops being a recommendation and becomes a migration you have to do.
Everywhere else it is a strong default, not a prohibition. Both runtimes run on the same Fluid Compute infrastructure, in the same regions, under the same Active CPU pricing — so in nearly every case Edge gains you nothing while costing you most of the Node.js API surface. If you have a specific, tested reason to stay on Edge, that's a legitimate call; just make it deliberately rather than by habit.
### The default to reach for
```ts
// app/api/hello/route.ts — no runtime export needed.
export async function GET() {
return Response.json({ message: 'Hello from Node.js on Fluid Compute' })
}
```
Node.js is the default. Omit `export const runtime` entirely rather than writing `export const runtime = 'nodejs'`.
### Reasons people reach for Edge — and what to do instead
| "I need Edge because…" | Reality | Do this instead |
|---|---|---|
| "…I need to stream / SSE / AI tokens" | Streaming is zero-config on Node.js. This is the single most common false belief. | Return a `ReadableStream` from a normal Node.js function |
| "…I need low latency" | Both run on Fluid Compute. Fluid pre-warms instances and caches bytecode; the difference is noise next to your DB/API round trips | Stay on Node.js; pin `regions` near your data |
| "…auth checks / redirects / A-B tests at the edge" | That's Routing Middleware's job, and **Routing Middleware supports full Node.js** — it is not edge-only | Use Routing Middleware (`routing-middleware` skill) |
| "…it's cheaper" | Identical Active CPU pricing | Stay on Node.js |
| "…it has faster cold starts" | Fluid Compute reuses warm instances across concurrent invocations and bytecode-caches Node 20+ in production | Stay on Node.js |
| "…my function must run globally" | Edge's global execution usually *hurts* — every DB query crosses an ocean | Single region (`iad1` default) next to your database |
### What Edge actually costs you
- No `fs`, no native modules, no `require()` — ESM only, and most npm packages with Node.js dependencies simply will not load
- No `eval` / `new Function` / dynamic `WebAssembly.instantiate`
- **Code size limit after gzip: 1 MB (Hobby), 2 MB (Pro), 4 MB (Enterprise)** — versus 250 MB uncompressed (up to 5 GB) on Node.js
- Must begin sending a response within **25 seconds** (it may then stream for up to 300s). The 300s/800s/1800s duration limits below apply to the Node.js, Bun, and Python runtimes — **not** to Edge
- No long-duration or large-function support of any kind
### Migrating an existing Edge function
Worth doing when you're already touching the file, and required on Next.js 16.3+. An Edge function that works today isn't an emergency.
1. Remove `export const runtime = 'edge'` (or `runtime: 'edge'` in `vercel.json` / the `config` object).
2. Replace `next/server` Edge-only imports where applicable; the Web `Request`/`Response` handler signature is unchanged, so most route handlers need no other edit.
3. If you pinned execution with the Edge-only `preferredRegion`, use `regions` in `vercel.json` instead.
4. Confirm Fluid Compute is on (default since April 23, 2025) and redeploy.
There is no rollback story to plan for: Node.js is a superset of what the function could do on Edge.
## Function Types
### Node.js (the default)
- Full Node.js runtime, all npm packages available
- Default for Next.js route handlers, Server Actions, Server Components, and any file in `/api`
- **Node.js 24 LTS is GA** for builds and functions (V8 13.6, global `URLPattern`, Undici v7, npm v11). **Node.js 20 is deprecated on October 1, 2026** — move off `nodejs20.x`
- Duration: 300s default on every plan; 800s max on Pro/Enterprise; 1800s with the extended-duration beta
### Bun
Add `"bunVersion": "1.x"` to `vercel.json` to run functions on Bun instead of Node.js. ~28% lower latency for CPU-bound workloads. Supports Next.js, Express, Hono, Nitro, and `Bun.serve` as an entrypoint. Bun supports both large functions and extended max duration.
### Python
Python 3.12 / 3.13 / 3.14 on Fluid Compute. FastAPI, Flask, and Django build into a **single** function from the resolved entrypoint — key `vercel.json` config on that entrypoint file (`app/main.py`, `myproject/wsgi.py`), not on `/api` routes. Python gets a **500 MB** standard bundle limit (vs. 250 MB) and supports large functions and extended duration.
### Rust
Rust functions run on Fluid Compute with HTTP streaming and Active CPU pricing. Official runtime (Beta) built on the `vercel_runtime` crate. Supports environment variables up to 64 KB.
### Container images (Docker)
Any OCI image via `Dockerfile.vercel`. See [Docker and Container Images](#docker-and-container-images) below.
### Edge (legacy — not recommended)
V8 isolates with a subset of Web APIs. Fine to leave in place on existing deployments, but not the runtime to pick for new work. See [Prefer Node.js over the Edge runtime](#prefer-nodejs-over-the-edge-runtime).
### Choosing a Runtime
| Need | Runtime | Why |
|------|---------|-----|
| Anything not listed below | `nodejs` | The default, and correct nearly always |
| Full Node.js APIs, npm packages | `nodejs` | Full compatibility |
| AI streaming, SSE, WebSockets | `nodejs` | Zero-config streaming, long durations |
| Lower latency, CPU-bound work | `nodejs` + Bun | ~28% latency reduction |
| Database connections, heavy deps | `nodejs` | Pin `regions` next to the database |
| Data/ML libraries, big model files | `nodejs` or `python` + large functions | Up to 5 GB bundles |
| Systems-level performance | `rust` | Native speed on Fluid Compute |
| Custom system libraries (FFmpeg, Chromium), Go/Ruby/PHP, unsupported frameworks | container image | Bring your own Dockerfile |
| Auth, redirects, A/B tests before the cache | Routing Middleware | Runs on Node.js, framework-agnostic |
| Hours-to-months of execution | Vercel Workflow | Durable steps, no duration limit |
`edge` is deliberately absent: there's no row here where it's the better answer for new code.
## Fluid Compute
Fluid Compute is the execution model for Vercel Functions — **enabled by default for new projects since April 23, 2025**, and available for the Node.js, Python, Bun, Rust, and Edge runtimes. Enable it explicitly per-deployment with `{"fluid": true}` in `vercel.json`, or project-wide in Settings → Functions.
Long-duration, large-function, and container-image support all depend on it.
Key behaviors:
- **Optimized concurrency**: multiple invocations share one instance instead of one microVM per request. Vercel prioritizes idle existing resources before allocating new ones. Available on the Node.js and Python runtimes.
- **Active CPU pricing**: you are billed for CPU time your code actually consumes, plus provisioned memory while requests are in flight, plus invocations. Waiting on I/O (AI models, DB queries) does not accrue Active CPU — which is what makes 30-minute functions affordable.
- **Automatic cold start optimization**: function pre-warming plus **bytecode caching** on Node.js 20+. Bytecode caching applies to **production only** — not dev or preview, so don't benchmark cold starts in a preview deployment.
- **Error isolation**: an uncaught exception or unhandled rejection is logged and in-flight requests are allowed to finish; one broken request will not crash its neighbors on the same instance.
- **Cross-AZ and cross-region failover**: fails over to another availability zone in-region first, then to the next closest region.
- **Graceful shutdown**: `SIGTERM` before termination (see below).
### Instance Sizes (memory / CPU)
| Type | Memory / CPU | Use |
|------|--------------|-----|
| Standard (default) | 2 GB / 1 vCPU | Predictable performance for production workloads |
| Performance | 4 GB / 2 vCPU | Latency-sensitive applications and SSR workloads |
- **With Fluid Compute enabled, memory cannot be set in `vercel.json`** — setting it there produces a build-time warning. Set it in the dashboard instead: Settings → Functions → Advanced Settings → **Function CPU**, then redeploy. (The `memory` key still exists for legacy non-Fluid deployments, which is why you will find older examples using it.)
- **Pro/Enterprise only.** Hobby always runs Standard (2 GB / 1 vCPU) and cannot configure it. The Basic instance has been removed.
- More memory also means more CPU, which can *reduce* Active CPU billing for CPU-bound work by finishing sooner — but it raises Provisioned Memory cost while requests are in flight.
- Projects created before 2019-11-08 may still sit on legacy sizes (1024 MB / 0.6 vCPU on Hobby, 3008 MB / 1.67 vCPU on Pro) until you pick a size in the dashboard.
### Settings precedence
Function code (`export const maxDuration`) → `vercel.json` → dashboard → Fluid defaults. Later entries lose.
### Background Processing with `waitUntil`
`waitUntil` takes a **Promise**, not a callback. Passing a function does nothing — a common and silent bug.
```ts
import { waitUntil } from '@vercel/functions'
export async function POST(req: Request) {
const data = await req.json()
// Correct: invoke the async work and hand over the promise.
waitUntil(processAnalytics(data))
// For several tasks, combine them:
waitUntil(Promise.all([sendNotification(data), updateCache(data)]))
return Response.json({ received: true })
}
```
### Next.js `after` (equivalent)
```ts
import { after } from 'next/server'
export async function POST(req: Request) {
const data = await req.json()
after(async () => {
await logToAnalytics(data)
})
return Response.json({ ok: true })
}
```
### Graceful shutdown and request cancellation
```ts
// Runs on scale-down. 500 ms to clean up (30 s for container images).
process.on('SIGTERM', () => {
// flush buffers, close pools
})
```
Request cancellation is **opt-in**, per path. With it enabled, a client disconnect aborts `request.signal` and terminates the function — anything not wrapped in `waitUntil`/`after` is lost, which is exactly why it is not on by default.
```json filename="vercel.json"
{
"functions": {
"api/*": { "supportsCancellation": true }
}
}
```
```ts
export async function GET(request: Request) {
// Pass the signal through so upstream work stops too.
const res = await fetch('https://upstream.example.com', { signal: request.signal })
return new Response(res.body, { status: res.status })
}
```
## Duration and Long-Duration Functions
### Duration limits
With Fluid Compute (default), per [Vercel's limits](https://vercel.com/docs/functions/limitations#max-duration):
| Plan | Default | Maximum | Extended maximum |
|------|---------|---------|------------------|
| Hobby | 300s (5 min) | 300s (5 min) | — |
| Pro | 300s (5 min) | 800s | 1800s (30 min) — Beta |
| Enterprise | 300s (5 min) | 800s | 1800s (30 min) — Beta |
The 800s maximum is **generally available** on Pro and Enterprise. The 1800s extended maximum is **in beta**. Exceeding the limit returns `504 FUNCTION_INVOCATION_TIMEOUT`.
**Hobby's default and maximum are the same 300s** — there is no headroom to raise, and no extended duration. Setting `maxDuration` above 300s on Hobby does nothing; upgrade to Pro.
### Setting `maxDuration`
```ts
// app/api/report/route.ts — Next.js App Router (and Node.js, SvelteKit, Astro,
// Nuxt, Remix via their own config). Value is in seconds.
export const maxDuration = 800
export async function POST(request: Request) {
return Response.json({ ok: true })
}
```
For other frameworks and runtimes — Next.js < 13.5, Rust, Go, Python, Ruby — use `vercel.json`:
```json
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"functions": {
"api/long-task.py": { "maxDuration": 1800 }
}
}
```
Glob order matters, and Next.js projects using `src/` must prefix paths with `src/`. For Python frameworks, key on the resolved entrypoint (`app/main.py`), not an `/api` route.
To change the project-wide default: Settings → Functions → **Function Max Duration**.
### Extended max duration (30 minutes) — Beta
Pro and Enterprise teams can run individual functions for up to **1800s**. Requirements, all of which are load-bearing:
- **Per-function configuration only.** Durations above 800s must be set in code or in `vercel.json` for that function. **Project-level defaults above 800s are not supported** during the beta — raising the dashboard default will not get you to 1800s.
- **Supported runtimes only**: `nodejs20.x`, `nodejs22.x`, `nodejs24.x`, Bun `1.x` and `1.4.x`, `python3.12`, `python3.13`, `python3.14`.
- **Fluid Compute must be enabled** (default for new projects).
- **Secure Compute and Static IPs do not support durations above 800s** during the beta. If the project uses either, you are capped at 800s.
```ts
// app/api/long-task/route.ts
export const maxDuration = 1800 // 30 minutes
export async function POST(request: Request) {
await doTheLongThing()
return Response.json({ ok: true })
}
```
### Keeping a long request alive
Over HTTP/2, Vercel sends connection-level `PING` frames while the response is idle. **HTTP/1.1 has no equivalent**, so HTTP/1.1 clients and intermediate proxies may still close an idle connection long before 30 minutes elapse. For any long-running handler, **stream progress or heartbeat data while the work runs** rather than going silent and emitting one payload at the end.
Use `getDeadline()` to find out how much time is actually left and bail out cleanly:
```ts
import { getDeadline } from '@vercel/functions'
const deadline = getDeadline() // Date | undefined (undefined outside the Vercel Functions runtime)
const msRemaining = deadline ? deadline.getTime() - Date.now() : Infinity
```
### Cost of long functions
Active CPU pricing is what makes this viable: a 25-minute function that spends 24 minutes awaiting an LLM bills almost no Active CPU, only Provisioned Memory for the instance while the request is in flight.
### When 30 minutes is not enough
Do not chain functions, self-invoke, or poll to fake durability. Use **Vercel Workflow**, which pauses, resumes, and keeps state for minutes to months with no duration limit, plus automatic retries and crash safety. Rough guide:
- ≤ 300s → any plan, no configuration needed
- 300–800s → Pro/Enterprise, set `maxDuration`
- 800–1800s → Pro/Enterprise, extended-duration beta, per-function config
- Beyond 30 min, or needs to survive a crash/deploy → Vercel Workflow (`workflow` skill)
Workflow steps themselves support extended function durations, so a single step can also run up to 30 minutes.
## Large Functions (bundle size)
### Standard limits
| Runtime | Uncompressed bundle limit |
|---------|---------------------------|
| Node.js, Bun, Rust, Go | 250 MB (includes runtime layers) |
| Python | 500 MB |
| Edge runtime | 1 MB Hobby / 2 MB Pro / 4 MB Enterprise, **after gzip** |
Blowing the limit fails the build with `Serverless Function has exceeded the unzipped maximum size of 250 MB`.
### Large functions — Beta
Large functions raise the uncompressed bundle ceiling to **5 GB**. This is what makes Python data/AI libraries, model weights, browser automation (Playwright/Puppeteer), image/video processing, and big backend apps deployable as Functions.
- **Runtimes**: Node.js, Bun, Python.
- **Requires Fluid Compute with Active CPU** enabled (default for new projects).
- **New projects are eligible by default.** Existing projects opt in with the `VERCEL_SUPPORT_LARGE_FUNCTIONS` environment variable, then redeploy:
```bash
vercel env add VERCEL_SUPPORT_LARGE_FUNCTIONS # value: 1 (use 0 to disable)
```
The environment variable always takes precedence over the project default, in both directions.
- **Only functions that exceed the standard limit use the large-function path** — everything under 250 MB keeps the normal, faster path, so enabling it is not a global performance trade.
- **Not supported with Secure Compute or Static IPs.**
View on GitHub