{"id":27068,"plugin_id":"plugin_connector_690a90ec05c881918afb6a55dc9bbaa1","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-06T18:03:11.655Z","digest":"43313075f379edfe1fd9c7541e36103a5b66f49f01b4e51f9df56e17812c0cf0","against":24894,"payload":{"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.","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":182}],"name":"vercel-functions","skill_md_contents":"---\nname: vercel-functions\ndescription: 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.\nsummary: \"Vercel Functions run on Fluid Compute with Node.js as the default runtime — strongly prefer it over `runtime = 'edge'` (Vercel recommends migrating off Edge, and Next.js 16.3+ no longer supports it). Duration: 300s default on every plan including Hobby, 800s max on Pro/Enterprise, 1800s (30 min) per-function in the extended beta; beyond that use Vercel Workflow. Bundles: 250 MB standard (500 MB Python), 5 GB via the large functions beta (`VERCEL_SUPPORT_LARGE_FUNCTIONS=1`). Request/response bodies cap at 4.5 MB. Memory is dashboard-only (Standard 2 GB/1 vCPU, Performance 4 GB/2 vCPU; Hobby fixed). Docker works: add `Dockerfile.vercel` to run an OCI image as an autoscaling, stateless, scale-to-zero Function.\"\nmetadata:\n  priority: 8\n  docs:\n    - \"https://vercel.com/docs/functions\"\n    - \"https://vercel.com/docs/functions/runtimes\"\n    - \"https://vercel.com/docs/functions/limitations\"\n    - \"https://vercel.com/docs/functions/configuring-functions/duration\"\n    - \"https://vercel.com/docs/functions/container-images\"\n    - \"https://vercel.com/docs/fluid-compute\"\n    - \"https://vercel.com/docs/functions/websockets\"\n  sitemap: \"https://vercel.com/sitemap.xml\"\n  pathPatterns:\n    - 'api/**/*.*'\n    - 'pages/api/**'\n    - 'src/pages/api/**'\n    - 'app/**/route.*'\n    - 'src/app/**/route.*'\n    - 'apps/*/api/**/*.*'\n    - 'apps/*/app/**/route.*'\n    - 'apps/*/src/app/**/route.*'\n    - 'apps/*/pages/api/**'\n    - 'vercel.json'\n    - 'apps/*/vercel.json'\n    - 'vercel.ts'\n    - 'apps/*/vercel.ts'\n    # Vercel builds `Dockerfile.vercel` / `Containerfile.vercel` into a\n    # container-image Function, so these are Functions config, not generic Docker.\n    - 'Dockerfile.vercel'\n    - '*/Dockerfile.vercel'\n    - 'Containerfile.vercel'\n    - '*/Containerfile.vercel'\n  bashPatterns:\n    - '\\bvercel\\s+dev\\b'\n    - '\\bvercel\\s+logs\\b'\n    - '\\bvercel\\s+vcr\\b'\n  importPatterns:\n    - 'ws'\n    - 'socket.io'\n    - 'socket.io-client'\n  promptSignals:\n    phrases:\n      - \"websocket\"\n      - \"websockets\"\n      - \"web socket\"\n      - \"socket.io\"\n      # Polling is the classic technique people reach for when they think Vercel\n      # lacks websockets. We intentionally do NOT trigger on named third-party\n      # services (Pusher, PubNub, Ably) — those are deliberate choices, not a\n      # signal that someone is working around a missing feature.\n      - \"long polling\"\n      - \"long-polling\"\n      # Duration: people hit the ceiling and ask about it in these words.\n      - \"maxduration\"\n      - \"max duration\"\n      - \"function timeout\"\n      - \"function times out\"\n      - \"long-running function\"\n      - \"long running function\"\n      - \"504\"\n      # Bundle size: the 250 MB error message is the usual entry point.\n      - \"unzipped maximum size\"\n      - \"250mb\"\n      - \"250 mb\"\n      - \"large function\"\n      - \"bundle size limit\"\n      # Containers: Dockerfile.vercel is a Functions feature, not just packaging.\n      - \"dockerfile\"\n      - \"container image\"\n      - \"container registry\"\n      - \"runtime edge\"\n      - \"edge runtime\"\n    allOf:\n      - [\"docker\", \"vercel\"]\n      - [\"hobby\", \"limit\"]\n    anyOf:\n      - \"realtime\"\n      - \"bidirectional\"\n      - \"ws server\"\n      - \"polling\"\n      - \"server-sent events\"\n      - \"fluid compute\"\n      - \"active cpu\"\n    noneOf: []\n    minScore: 6\nvalidate:\n  -\n    pattern: export\\s+default\\s+function\n    message: 'Use named exports (GET, POST, PUT, DELETE) instead of default export for route handlers'\n    severity: error\n    # Skip on App Router page / layout / loading / error / not-found / sitemap / template / default files,\n    # which require a default export by Next.js convention. Detected via the 'use client' directive,\n    # an App Router config export (metadata, dynamic, revalidate, fetchCache, runtime), an `export default\n    # function` whose name matches an App Router file (Page / Layout / Loading / etc.), or any JSX\n    # element with a capitalised component tag — all signals that the file is a page-style file rather\n    # than a route handler. See anthropics/claude-code#54989.\n    skipIfFileContains: \"(?:^|\\\\n)\\\\s*['\\\"]use\\\\s+client['\\\"]|export\\\\s+const\\\\s+(?:metadata|dynamic|revalidate|fetchCache|runtime)\\\\b|export\\\\s+default\\\\s+(?:async\\\\s+)?function\\\\s+\\\\w*(?:Page|Layout|Loading|Error|NotFound|Sitemap|Template|Default|sitemap|robots|opengraph|manifest)\\\\b|<[A-Z][A-Za-z0-9]*|\\\\{\\\\s*children\\\\s*[,}:]|MetadataRoute\\\\.|from\\\\s+['\\\"]next/(?:font|image|link|navigation|headers|cookies)['\\\"]\"\n  -\n    pattern: NextApiRequest|NextApiResponse\n    message: 'NextApiRequest/NextApiResponse are Pages Router types — use Web API Request/Response'\n    severity: error\n  -\n    # Vercel's docs recommend migrating off the Edge runtime, and Next.js 16.3+\n    # doesn't support it. Surfaced as a recommendation rather than an error:\n    # existing Edge functions still work, so this is a nudge, not a blocker.\n    # Matches `\"runtime\": \"edge\"` in vercel.json too.\n    pattern: 'runtime[''\"]?\\s*[=:]\\s*[''\"]edge[''\"]'\n    message: 'Consider dropping `runtime = \"edge\"`. Vercel recommends migrating from Edge to Node.js, and Next.js 16.3+ no longer supports it. Node.js on Fluid Compute runs in the same regions at the same price with full Node.js APIs, longer durations, and larger bundles.'\n    severity: recommended\n  -\n    pattern: 'from\\s+[''\"](openai|@anthropic-ai/sdk|anthropic)[''\"]|new\\s+(OpenAI|Anthropic)\\('\n    message: 'Direct AI provider SDK detected in route handler. Use the Vercel AI SDK for streaming, tools, and provider abstraction.'\n    severity: recommended\n    upgradeToSkill: ai-sdk\n    upgradeWhy: 'Replace vendor-locked provider SDKs with @ai-sdk/openai or @ai-sdk/anthropic for unified streaming and tool support.'\n    skipIfFileContains: '@ai-sdk/|from\\s+[''\"](ai)[''\"]|import.*from\\s+[''\"](ai)[''\"]|streamText|generateText'\n  -\n    pattern: 'setTimeout\\s*\\(|setInterval\\s*\\(|await\\s+new\\s+Promise\\s*\\([^)]*setTimeout'\n    message: 'Long-running or polling logic detected in a serverless handler. Functions have execution time limits.'\n    severity: recommended\n    upgradeToSkill: workflow\n    upgradeWhy: 'Move delayed/polling logic to Vercel Workflow for durable execution with pause, resume, retries, and crash safety.'\n    skipIfFileContains: 'use workflow|use step'\n  -\n    pattern: 'writeFile(Sync)?\\(|createWriteStream\\(|from\\s+[''\"](multer|formidable)[''\"]|fs\\.writeFile'\n    message: 'Local filesystem write detected. Serverless functions have ephemeral, read-only filesystems.'\n    severity: error\n    upgradeToSkill: vercel-storage\n    upgradeWhy: 'Replace local filesystem writes with Vercel Blob, Neon, or Upstash for persistent, platform-native storage.'\n    skipIfFileContains: '@vercel/blob|@upstash/|@neondatabase/'\n  -\n    pattern: 'export\\s+(async\\s+)?function\\s+(GET|POST|PUT|PATCH|DELETE)\\b'\n    message: 'Route handler has no observability instrumentation. Add logging and error tracking for production debugging.'\n    severity: warn\n    skipIfFileContains: 'console\\.error|logger\\.|captureException|Sentry|@vercel/otel|withTracing'\n  -\n    pattern: 'from\\s+[''\"\"](lru-cache|node-cache|memory-cache)[''\"\"]|new\\s+(LRUCache|NodeCache|Map)\\(\\s*\\).*cache'\n    message: 'In-process memory cache detected in serverless function. Process memory is not shared across invocations.'\n    severity: recommended\n    upgradeToSkill: runtime-cache\n    upgradeWhy: 'Replace in-process caches with Vercel Runtime Cache (getCache from @vercel/functions) for region-aware caching that persists across invocations.'\n    skipIfFileContains: 'getCache|from\\s+[''\"\"]\\@vercel/functions[''\"\"]'\n  -\n    pattern: 'maxRetries\\s*[=:]|retryCount\\s*[=:]|retry\\s*\\(\\s*|for\\s*\\([^)]*retry|while\\s*\\([^)]*retry'\n    message: 'Manual retry logic detected. Use Vercel Workflow SDK for automatic retries with durable execution.'\n    severity: recommended\n    upgradeToSkill: workflow\n    upgradeWhy: 'Replace manual retry loops with Workflow SDK steps that provide automatic retries, crash safety, and observability.'\n    skipIfFileContains: 'use workflow|use step|from\\s+[''\"\"](workflow)[''\"\"]'\n  -\n    pattern: 'from\\s+[''\"](express)[''\"\"]|require\\s*\\(\\s*[''\"](express)[''\"\"\\)]'\n    message: 'Express.js detected in a Vercel project. Vercel Functions use the Web Request/Response API — Express middleware, req/res, and app.listen() do not work in serverless.'\n    severity: recommended\n    upgradeToSkill: vercel-functions\n    upgradeWhy: 'Replace Express with Next.js route handlers (export async function GET/POST) or Vercel Functions using the Web Request/Response API.'\n    skipIfFileContains: 'export\\s+(async\\s+)?function\\s+(GET|POST|PUT|PATCH|DELETE)|from\\s+[''\"\"](next/server|@vercel/functions)[''\"\"]'\nretrieval:\n  aliases:\n    - serverless functions\n    - api routes\n    - edge functions\n    - lambda\n    - websockets\n    - socket.io\n    - docker\n    - dockerfile\n    - container images\n    - function timeout\n    - max duration\n    - bundle size\n    - hobby limits\n  intents:\n    - create serverless function\n    - configure function runtime\n    - optimize cold starts\n    - add api route\n    - serve a websocket connection\n    - run a function for longer than 5 minutes\n    - deploy a dockerfile\n    - fix a function that exceeds the bundle size limit\n    - check plan limits for functions\n  entities:\n    - Serverless Functions\n    - Edge Functions\n    - Fluid Compute\n    - Long-duration functions\n    - Large functions\n    - Container Images\n    - Vercel Container Registry\n    - streaming\n    - WebSockets\n    - Cron Jobs\nchainTo:\n  -\n    pattern: 'from\\s+[''\\\"](openai|@anthropic-ai/sdk|anthropic)[''\"]|new\\s+(OpenAI|Anthropic)\\('\n    targetSkill: ai-sdk\n    message: 'Direct AI provider SDK in route handler — loading AI SDK guidance for unified streaming and tool support.'\n  -\n    pattern: 'setTimeout\\s*\\(|setInterval\\s*\\(|await\\s+new\\s+Promise\\s*\\([^)]*setTimeout'\n    targetSkill: workflow\n    message: 'Long-running or polling logic in serverless handler — loading Workflow SDK for durable execution.'\n  -\n    pattern: 'writeFile(Sync)?\\(|createWriteStream\\(|from\\s+[''\\\"](multer|formidable)[''\"]|fs\\.writeFile'\n    targetSkill: vercel-storage\n    message: 'Local filesystem write in serverless function — loading Vercel Storage guidance for platform-native persistence.'\n  -\n    pattern: 'from\\s+[''\"\"]@vercel/(postgres|kv)[''\"\"]'\n    targetSkill: vercel-storage\n    message: '@vercel/postgres and @vercel/kv are sunset — loading Vercel Storage guidance for Neon and Upstash migration.'\n  -\n    pattern: 'generateObject\\s*\\(|streamObject\\s*\\(|toDataStreamResponse|maxSteps\\b|CoreMessage\\b'\n    targetSkill: ai-sdk\n    message: 'Deprecated AI SDK v5 API detected — loading AI SDK guidance for migration.'\n  -\n    pattern: 'while\\s*\\(\\s*true\\s*\\)\\s*\\{|for\\s*\\(\\s*;\\s*;\\s*\\)\\s*\\{|setInterval\\s*\\(\\s*async'\n    targetSkill: workflow\n    message: 'Polling loop in serverless function detected — loading Workflow SDK for durable, crash-safe execution with pause/resume.'\n    skipIfFileContains: \"use workflow|use step|from\\\\s+['\\\"]workflow['\\\"]\"\n  -\n    pattern: \"from\\\\s+['\\\"]express['\\\"]|require\\\\s*\\\\(\\\\s*['\\\"]express['\\\"]\"\n    targetSkill: vercel-functions\n    message: 'Express.js detected — loading Vercel Functions guidance for Web Request/Response API route handlers that replace Express middleware and routing.'\n    skipIfFileContains: \"export\\\\s+(async\\\\s+)?function\\\\s+(GET|POST|PUT|PATCH|DELETE)\"\n  -\n    pattern: 'from\\s+[''\"\"](lru-cache|node-cache|memory-cache)[''\"\"]|new\\s+(LRUCache|NodeCache|Map)\\(\\s*\\).*cache'\n    targetSkill: runtime-cache\n    message: 'In-process memory cache in serverless function — loading Runtime Cache guidance for region-aware caching that persists across invocations.'\n    skipIfFileContains: 'getCache|from\\s+[''\"\"]\\@vercel/functions[''\"\"]'\n  -\n    pattern: 'maxRetries\\s*[=:]|retryCount\\s*[=:]|retry\\s*\\(\\s*|for\\s*\\([^)]*retry|while\\s*\\([^)]*retry'\n    targetSkill: workflow\n    message: 'Manual retry logic in serverless handler — loading Workflow SDK guidance for automatic retries with durable execution.'\n    skipIfFileContains: 'use workflow|use step|from\\s+[''\"\"](workflow)[''\"\"]'\n\n  -\n    pattern: 'import\\s*\\{[^}]*\\bmetric\\b[^}]*\\}\\s*from\\s*[''\"]@vercel/functions[''\"]'\n    targetSkill: custom-metrics\n    message: '@vercel/functions metric() detected — loading Custom Metrics guidance for naming, attributes, and vc metrics queries.'\n\n---\n\n# Vercel Functions\n\nYou are an expert in Vercel Functions — the compute layer of the Vercel platform.\n\n## Prefer Node.js over the Edge runtime\n\n**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.**\n\nThis 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.\n\nEverywhere 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.\n\n### The default to reach for\n\n```ts\n// app/api/hello/route.ts — no runtime export needed.\nexport async function GET() {\n  return Response.json({ message: 'Hello from Node.js on Fluid Compute' })\n}\n```\n\nNode.js is the default. Omit `export const runtime` entirely rather than writing `export const runtime = 'nodejs'`.\n\n### Reasons people reach for Edge — and what to do instead\n\n| \"I need Edge because…\" | Reality | Do this instead |\n|---|---|---|\n| \"…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 |\n| \"…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 |\n| \"…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) |\n| \"…it's cheaper\" | Identical Active CPU pricing | Stay on Node.js |\n| \"…it has faster cold starts\" | Fluid Compute reuses warm instances across concurrent invocations and bytecode-caches Node 20+ in production | Stay on Node.js |\n| \"…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 |\n\n### What Edge actually costs you\n\n- No `fs`, no native modules, no `require()` — ESM only, and most npm packages with Node.js dependencies simply will not load\n- No `eval` / `new Function` / dynamic `WebAssembly.instantiate`\n- **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\n- 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\n- No long-duration or large-function support of any kind\n\n### Migrating an existing Edge function\n\nWorth 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.\n\n1. Remove `export const runtime = 'edge'` (or `runtime: 'edge'` in `vercel.json` / the `config` object).\n2. Replace `next/server` Edge-only imports where applicable; the Web `Request`/`Response` handler signature is unchanged, so most route handlers need no other edit.\n3. If you pinned execution with the Edge-only `preferredRegion`, use `regions` in `vercel.json` instead.\n4. Confirm Fluid Compute is on (default since April 23, 2025) and redeploy.\n\nThere is no rollback story to plan for: Node.js is a superset of what the function could do on Edge.\n\n## Function Types\n\n### Node.js (the default)\n- Full Node.js runtime, all npm packages available\n- Default for Next.js route handlers, Server Actions, Server Components, and any file in `/api`\n- **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`\n- Duration: 300s default on every plan; 800s max on Pro/Enterprise; 1800s with the extended-duration beta\n\n### Bun\nAdd `\"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.\n\n### Python\nPython 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.\n\n### Rust\nRust 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.\n\n### Container images (Docker)\nAny OCI image via `Dockerfile.vercel`. See [Docker and Container Images](#docker-and-container-images) below.\n\n### Edge (legacy — not recommended)\nV8 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).\n\n### Choosing a Runtime\n\n| Need | Runtime | Why |\n|------|---------|-----|\n| Anything not listed below | `nodejs` | The default, and correct nearly always |\n| Full Node.js APIs, npm packages | `nodejs` | Full compatibility |\n| AI streaming, SSE, WebSockets | `nodejs` | Zero-config streaming, long durations |\n| Lower latency, CPU-bound work | `nodejs` + Bun | ~28% latency reduction |\n| Database connections, heavy deps | `nodejs` | Pin `regions` next to the database |\n| Data/ML libraries, big model files | `nodejs` or `python` + large functions | Up to 5 GB bundles |\n| Systems-level performance | `rust` | Native speed on Fluid Compute |\n| Custom system libraries (FFmpeg, Chromium), Go/Ruby/PHP, unsupported frameworks | container image | Bring your own Dockerfile |\n| Auth, redirects, A/B tests before the cache | Routing Middleware | Runs on Node.js, framework-agnostic |\n| Hours-to-months of execution | Vercel Workflow | Durable steps, no duration limit |\n\n`edge` is deliberately absent: there's no row here where it's the better answer for new code.\n\n## Fluid Compute\n\nFluid 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.\n\nLong-duration, large-function, and container-image support all depend on it.\n\nKey behaviors:\n- **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.\n- **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.\n- **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.\n- **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.\n- **Cross-AZ and cross-region failover**: fails over to another availability zone in-region first, then to the next closest region.\n- **Graceful shutdown**: `SIGTERM` before termination (see below).\n\n### Instance Sizes (memory / CPU)\n\n| Type | Memory / CPU | Use |\n|------|--------------|-----|\n| Standard (default) | 2 GB / 1 vCPU | Predictable performance for production workloads |\n| Performance | 4 GB / 2 vCPU | Latency-sensitive applications and SSR workloads |\n\n- **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.)\n- **Pro/Enterprise only.** Hobby always runs Standard (2 GB / 1 vCPU) and cannot configure it. The Basic instance has been removed.\n- 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.\n- 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.\n\n### Settings precedence\n\nFunction code (`export const maxDuration`) → `vercel.json` → dashboard → Fluid defaults. Later entries lose.\n\n### Background Processing with `waitUntil`\n\n`waitUntil` takes a **Promise**, not a callback. Passing a function does nothing — a common and silent bug.\n\n```ts\nimport { waitUntil } from '@vercel/functions'\n\nexport async function POST(req: Request) {\n  const data = await req.json()\n\n  // Correct: invoke the async work and hand over the promise.\n  waitUntil(processAnalytics(data))\n\n  // For several tasks, combine them:\n  waitUntil(Promise.all([sendNotification(data), updateCache(data)]))\n\n  return Response.json({ received: true })\n}\n```\n\n### Next.js `after` (equivalent)\n\n```ts\nimport { after } from 'next/server'\n\nexport async function POST(req: Request) {\n  const data = await req.json()\n\n  after(async () => {\n    await logToAnalytics(data)\n  })\n\n  return Response.json({ ok: true })\n}\n```\n\n### Graceful shutdown and request cancellation\n\n```ts\n// Runs on scale-down. 500 ms to clean up (30 s for container images).\nprocess.on('SIGTERM', () => {\n  // flush buffers, close pools\n})\n```\n\nRequest 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.\n\n```json filename=\"vercel.json\"\n{\n  \"functions\": {\n    \"api/*\": { \"supportsCancellation\": true }\n  }\n}\n```\n\n```ts\nexport async function GET(request: Request) {\n  // Pass the signal through so upstream work stops too.\n  const res = await fetch('https://upstream.example.com', { signal: request.signal })\n  return new Response(res.body, { status: res.status })\n}\n```\n\n## Duration and Long-Duration Functions\n\n### Duration limits\n\nWith Fluid Compute (default), per [Vercel's limits](https://vercel.com/docs/functions/limitations#max-duration):\n\n| Plan | Default | Maximum | Extended maximum |\n|------|---------|---------|------------------|\n| Hobby | 300s (5 min) | 300s (5 min) | — |\n| Pro | 300s (5 min) | 800s | 1800s (30 min) — Beta |\n| Enterprise | 300s (5 min) | 800s | 1800s (30 min) — Beta |\n\nThe 800s maximum is **generally available** on Pro and Enterprise. The 1800s extended maximum is **in beta**. Exceeding the limit returns `504 FUNCTION_INVOCATION_TIMEOUT`.\n\n**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.\n\n### Setting `maxDuration`\n\n```ts\n// app/api/report/route.ts — Next.js App Router (and Node.js, SvelteKit, Astro,\n// Nuxt, Remix via their own config). Value is in seconds.\nexport const maxDuration = 800\n\nexport async function POST(request: Request) {\n  return Response.json({ ok: true })\n}\n```\n\nFor other frameworks and runtimes — Next.js < 13.5, Rust, Go, Python, Ruby — use `vercel.json`:\n\n```json\n{\n  \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n  \"functions\": {\n    \"api/long-task.py\": { \"maxDuration\": 1800 }\n  }\n}\n```\n\nGlob 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.\n\nTo change the project-wide default: Settings → Functions → **Function Max Duration**.\n\n### Extended max duration (30 minutes) — Beta\n\nPro and Enterprise teams can run individual functions for up to **1800s**. Requirements, all of which are load-bearing:\n\n- **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.\n- **Supported runtimes only**: `nodejs20.x`, `nodejs22.x`, `nodejs24.x`, Bun `1.x` and `1.4.x`, `python3.12`, `python3.13`, `python3.14`.\n- **Fluid Compute must be enabled** (default for new projects).\n- **Secure Compute and Static IPs do not support durations above 800s** during the beta. If the project uses either, you are capped at 800s.\n\n```ts\n// app/api/long-task/route.ts\nexport const maxDuration = 1800 // 30 minutes\n\nexport async function POST(request: Request) {\n  await doTheLongThing()\n  return Response.json({ ok: true })\n}\n```\n\n### Keeping a long request alive\n\nOver 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.\n\nUse `getDeadline()` to find out how much time is actually left and bail out cleanly:\n\n```ts\nimport { getDeadline } from '@vercel/functions'\n\nconst deadline = getDeadline() // Date | undefined (undefined outside the Vercel Functions runtime)\nconst msRemaining = deadline ? deadline.getTime() - Date.now() : Infinity\n```\n\n### Cost of long functions\n\nActive 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.\n\n### When 30 minutes is not enough\n\nDo 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:\n\n- ≤ 300s → any plan, no configuration needed\n- 300–800s → Pro/Enterprise, set `maxDuration`\n- 800–1800s → Pro/Enterprise, extended-duration beta, per-function config\n- Beyond 30 min, or needs to survive a crash/deploy → Vercel Workflow (`workflow` skill)\n\nWorkflow steps themselves support extended function durations, so a single step can also run up to 30 minutes.\n\n## Large Functions (bundle size)\n\n### Standard limits\n\n| Runtime | Uncompressed bundle limit |\n|---------|---------------------------|\n| Node.js, Bun, Rust, Go | 250 MB (includes runtime layers) |\n| Python | 500 MB |\n| Edge runtime | 1 MB Hobby / 2 MB Pro / 4 MB Enterprise, **after gzip** |\n\nBlowing the limit fails the build with `Serverless Function has exceeded the unzipped maximum size of 250 MB`.\n\n### Large functions — Beta\n\nLarge 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.\n\n- **Runtimes**: Node.js, Bun, Python.\n- **Requires Fluid Compute with Active CPU** enabled (default for new projects).\n- **New projects are eligible by default.** Existing projects opt in with the `VERCEL_SUPPORT_LARGE_FUNCTIONS` environment variable, then redeploy:\n\n```bash\nvercel env add VERCEL_SUPPORT_LARGE_FUNCTIONS   # value: 1  (use 0 to disable)\n```\n\nThe environment variable always takes precedence over the project default, in both directions.\n\n- **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.\n- **Not supported with Secure Compute or Static IPs.**\n\n### Shrinking a bundle first\n\nA 5 GB function still costs you cold-start time. Trim before you opt in:\n\nIn `vercel.json` (not supported in Next.js — see below):\n\n```json filename=\"vercel.json\"\n{\n  \"functions\": {\n    \"api/**/*.py\": {\n      \"excludeFiles\": \"{tests/**,__tests__/**,**/*.test.py,fixtures/**,testdata/**}\"\n    }\n  }\n}\n```\n\n- Next.js ignores `includeFiles`/`excludeFiles` — use `outputFileTracingIncludes` / `outputFileTracingExcludes` in `next.config.js` instead.\n- Audit heavy imports, prefer dynamic `import()`, and check for a package in `dependencies` that belongs in `devDependencies`.\n\n### Request and response payloads\n\nBundle size is not payload size. The **request or response body of a Function is capped at 4.5 MB**; exceeding it returns `413 FUNCTION_PAYLOAD_TOO_LARGE`. For larger data:\n\n- **Uploads** → Vercel Blob **client uploads**, which send the file browser → Blob directly, bypassing the function\n- **Large responses** → stream them; streamed responses are not subject to the limit\n- Otherwise, chunk across multiple requests\n\n## Docker and Container Images\n\nVercel Functions run **OCI-compatible container images**. This is first-class Docker support: bring a Dockerfile, get an autoscaling function with scale-to-zero and Active CPU pricing. It is *not* a VM or a long-lived server.\n\n### Quick start\n\nCreate `Dockerfile.vercel` (or `Containerfile.vercel`) at the project root. Vercel detects it automatically and adds a rewrite routing all traffic to the image.\n\n```docker\n# Dockerfile.vercel\nFROM node:26-alpine\n\nRUN npm i -g srvx\nWORKDIR /app\nCOPY server.ts .\n\n# srvx listens on $PORT by default\nCMD [\"srvx\", \"--prod\"]\n```\n\n```ts\n// server.ts\nexport default {\n  fetch(req: Request) {\n    return Response.json({ ip: req.headers.get('x-forwarded-for') })\n  },\n}\n```\n\nDeploy with `vercel deploy` or a Git push. During the build, the image is built and pushed to [Vercel Container Registry (VCR)](https://vercel.com/docs/container-registry).\n\n### The rules that actually bite\n\n- **Serve HTTP on port 80**, or override with the `PORT` environment variable in project settings. A container that doesn't listen gets no traffic.\n- **Containers must be stateless.** Each instance takes a request, returns a response, and keeps nothing between calls — that is what allows autoscaling and scale-to-zero. Persist to a Marketplace database, Redis, or Blob; never to the container filesystem.\n- **Scale to zero** after 5 minutes without traffic in production, 30 seconds in preview. Cold starts are real; do not assume a warm process.\n- **`SIGTERM` with a 30-second grace period** on scale-down (regular functions get 500 ms). Use it to drain.\n- **Logs are not per-request.** `stdout`/`stderr` are broadcast to all inflight requests of the instance, so correlate with your own request IDs.\n- **Same Function limits and Active CPU pricing** apply for size, memory, and duration.\n- **Secure Compute and Static IPs are not supported** with custom container images. If you need either, deploy that part without a container.\n- **Local dev**: `vercel dev` runs the image and requires the `docker` CLI plus a running daemon.\n\n### Multiple services in one project\n\nUse [Services](https://vercel.com/docs/services) to deploy several frontends/backends in one project, containerized or not. Set `runtime: \"container\"` on any service you want built as an image; `entrypoint` points at the Dockerfile relative to that service's `root`.\n\n```json filename=\"vercel.json\"\n{\n  \"services\": {\n    \"frontend\": { \"runtime\": \"container\", \"root\": \"frontend/\", \"entrypoint\": \"Dockerfile.vercel\" },\n    \"backend\":  { \"runtime\": \"container\", \"root\": \"backend/\",  \"entrypoint\": \"Dockerfile.vercel\" }\n  },\n  \"rewrites\": [\n    { \"source\": \"/api/(.*)\", \"destination\": { \"service\": \"backend\" } },\n    { \"source\": \"/(.*)\",     \"destination\": { \"service\": \"frontend\" } }\n  ]\n}\n```\n\nServices are internal by default — without a top-level rewrite, nothing is publicly routable. When `services` is present, build/runtime keys (`functions`, `buildCommand`, `installCommand`, `outputDirectory`, `framework`) move into the service and are no longer valid at the top level.\n\n### Vercel Container Registry (VCR)\n\n```bash\nvercel vcr login docker              # authenticate Docker with a short-lived OIDC token\nvercel vcr image ls my-app           # list images\nvercel vcr image inspect my-app <id>\nvercel vcr image rm my-app <id>\n```\n\nRegistry limits: 2 GB per compressed layer, 15 GB total image size, 4 MB manifest, 1 MB config blob. Layers must be gzip or zstd compressed — uncompressed OCI layers are rejected. Repositories per project: 10 (Hobby) / 1,000 (Pro) / 5,000 (Enterprise). Storage is billed at $0.10 per GB.\n\n### When to reach for a container\n\nGood fits: Go, Rust, Ruby, PHP, or other backends; apps needing system libraries like FFmpeg or Chromium; frameworks outside Vercel's auto-detection; guaranteed build/runtime parity across environments.\n\nPoor fits: anything that must hold state in-process, keep a daemon alive between requests, or run background work independent of a request. Reach for Workflow, Queues, or Cron for those.\n\nIf your framework is already auto-detected and you have no system-library needs, the standard build is simpler and faster — a Dockerfile is not an upgrade by default.\n\n## Plan Limits at a Glance\n\n| | Hobby | Pro | Enterprise |\n|---|---|---|---|\n| Duration (default / max) | 300s / **300s** | 300s / 800s | 300s / 800s |\n| Extended duration (beta) | — | 1800s | 1800s |\n| Memory / CPU | 2 GB / 1 vCPU, not configurable | Standard or Performance (4 GB / 2 vCPU) | Standard or Performance |\n| Bundle size | 250 MB (500 MB Python), 5 GB with large functions beta | same | same |\n| Concurrency | auto-scales to 30,000 | 30,000 | 100,000+ |\n| Regions | single region | up to 3 | all |\n| Edge code size (gzipped) | 1 MB | 2 MB | 4 MB |\n| VCR repos per project | 10 | 1,000 | 5,000 |\n| Request/response body | 4.5 MB | 4.5 MB | 4.5 MB |\n\n### What changed for Hobby\n\nHobby function limits went **up substantially** with Fluid Compute, and stale 10s/60s numbers are a common source of bad advice:\n\n- **Duration: 60s → 300s for both the default and the maximum** — a 5× increase. Hobby functions can run a full five minutes.\n- **CPU: the Basic instance was removed; Hobby now runs Standard**, 1 vCPU / 2 GB (up from 1 vCPU / 1.7 GB), managed by Vercel with a minimum of 1 vCPU.\n- Hobby still cannot configure memory/CPU, use the extended 30-minute duration, or run in multiple regions — those remain Pro/Enterprise.\n\n## Streaming\n\nZero-config streaming on the default Node.js runtime, including Server-Sent Events (SSE). Essential for AI applications.\n\n> **You do NOT need `runtime = 'edge'` for streaming or SSE.** Streaming responses (`ReadableStream`, `text/event-stream`) work on the default Node.js runtime — this is the single most common reason people wrongly reach for Edge. Stay on Node.js (Fluid Compute) so you keep full Node.js APIs, npm packages, and longer durations; Edge offers no streaming advantage and caps you at 25s to first byte.\n\n```ts\nexport async function POST(req: Request) {\n  const encoder = new TextEncoder()\n  const stream = new ReadableStream({\n    async start(controller) {\n      for (const chunk of data) {\n        controller.enqueue(encoder.encode(chunk))\n        await new Promise(r => setTimeout(r, 100))\n      }\n      controller.close()\n    },\n  })\n\n  return new Response(stream, {\n    headers: { 'Content-Type': 'text/event-stream' },\n  })\n}\n```\n\nFor AI streaming, use the AI SDK's `toUIMessageStreamResponse()` (for chat UIs with `useChat`) which handles SSE formatting automatically.\n\n## WebSockets\n\nVercel Functions can hold open bidirectional WebSocket connections — use them for realtime features like interactive AI streaming, chat, and collaborative apps. There is **no separate WebSocket-server product and no third-party service (Pusher, Ably, etc.) required** — it runs on Vercel Functions directly. Requires **Fluid Compute**, which is the default for new projects.\n\n**How it works**: a WebSocket starts as an HTTP `GET` with an `Upgrade` header, so it passes through the same Routing Middleware, rewrites, Firewall rules, and rate limits as any other request. After the upgrade, the connection is pinned to a single function instance for its lifetime; Fluid Compute lets one instance serve many concurrent connections. Active CPU pricing means you're billed while processing messages, not for idle open connections — the same limits and pricing as other Function invocations apply.\n\n### `ws` (no extra config)\n\nWebSockets work like any distributed WebSocket server — export an `http.Server` and use a library such as `ws`:\n\n```ts\n// api/ws.ts\nimport http from 'http'\nimport { WebSocketServer } from 'ws'\n\nconst server = http.createServer()\nconst wss = new WebSocketServer({ server })\n\nwss.on('connection', (ws) => {\n  ws.on('message', (data) => ws.send(data)) // echo\n})\n\nexport default server\n```\n\n### Socket.IO\n\nHigher-level realtime libraries like Socket.IO work too. Configure the **client** to use the WebSocket transport directly — Socket.IO defaults to HTTP long-polling, which won't work:\n\n```ts\n// api/socket-io.ts\nimport http from 'http'\nimport { Server } from 'socket.io'\n\nconst server = http.createServer()\nconst io = new Server(server)\n\nio.on('connection', (socket) => {\n  socket.on('message', (data) => socket.send(data))\n})\n\nexport default server\n```\n\n```ts\n// client.ts\nimport { io } from 'socket.io-client'\n\nconst socket = io('https://your-domain.com', {\n  // Socket.IO appends /socket.io, so the full path becomes /api/socket-io/socket.io\n  path: '/api/socket-io/socket.io',\n  transports: ['websocket'], // required — Socket.IO defaults to HTTP long-polling\n})\n```\n\nExpress, Hono, and Nitro (including Nuxt, via native WebSocket support) serve WebSockets the same way — export the HTTP server. Python frameworks work too: FastAPI handles the upgrade natively, and `python-socketio` is protocol-compatible with the JS Socket.IO client.\n\n### Next.js\n\nNext.js doesn't expose an API for handling WebSocket upgrades. Use `experimental_upgradeWebSocket()` from `@vercel/functions` inside a route handler:\n\n```ts\n// app/api/ws/route.ts\nimport { experimental_upgradeWebSocket, type WebSocketData } from '@vercel/functions'\n\nexport async function GET() {\n  return experimental_upgradeWebSocket((ws) => {\n    ws.on('message', (data: WebSocketData) => ws.send(data))\n  })\n}\n```\n\n### Reconnects and persistent state\n\n- **Connections close when the function reaches its max duration.** Clients must reconnect with backoff, then resubscribe to channels and reload any state they need.\n- **No instance affinity across connections.** A reconnect — or a new deployment — may land on a different instance, so never keep durable state, presence, rooms, or pub/sub coordination in memory. Use an external store such as [Redis from the Marketplace](https://vercel.com/marketplace/redis).\n\n```ts\n// client.ts — reconnect with exponential backoff\nlet socket: WebSocket\nlet delay = 1000\n\nfunction connect() {\n  socket = new WebSocket('wss://your-domain.com/api/ws')\n  socket.addEventListener('open', () => { delay = 1000 })\n  socket.addEventListener('message', (e) => console.log(e.data))\n  socket.addEventListener('close', () => {\n    setTimeout(connect, delay)\n    delay = Math.min(delay * 2, 30000)\n  })\n}\n\nconnect()\n```\n\n## Cron Jobs\n\nSchedule function invocations via `vercel.json`:\n\n```json filename=\"vercel.json\"\n{\n  \"crons\": [\n    {\n      \"path\": \"/api/daily-report\",\n      \"schedule\": \"0 8 * * *\"\n    },\n    {\n      \"path\": \"/api/cleanup\",\n      \"schedule\": \"0 */6 * * *\"\n    }\n  ]\n}\n```\n\nThe cron endpoint receives a normal HTTP request. Verify it's from Vercel:\n\n```ts\nexport async function GET(req: Request) {\n  const authHeader = req.headers.get('authorization')\n  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {\n    return new Response('Unauthorized', { status: 401 })\n  }\n  // Do scheduled work\n  return Response.json({ ok: true })\n}\n```\n\n## Configuration\n\n`vercel.ts` is the recommended way to configure a project — full TypeScript types, dynamic logic, and env access via `@vercel/config`. `vercel.json` remains fully supported. Legacy `now.json` support ended **March 31, 2026**; rename it to `vercel.json` (no content changes required).\n\n```ts\n// vercel.ts\nimport type { VercelConfig } from '@vercel/config/v1'\n\nexport const config: VercelConfig = {\n  functions: {\n    'app/api/heavy/**': { maxDuration: 800 },\n    'app/api/report/**': { maxDuration: 1800 }, // Pro/Ent extended-duration beta\n  },\n  crons: [{ path: '/api/cleanup', schedule: '0 0 * * *' }],\n}\n```\n\nThe `vercel.json` equivalent:\n\n```json\n{\n  \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n  \"functions\": {\n    \"app/api/heavy/**\": { \"maxDuration\": 800 },\n    \"api/upload.js\": { \"supportsCancellation\": true }\n  }\n}\n```\n\nWhat you **cannot** put here:\n- `memory` — with Fluid Compute (the default), set it in the dashboard; Pro/Enterprise only, and `vercel.json` warns at build time\n- A project-wide default above 800s — extended durations are per-function only\n\n`runtime: \"edge\"` is accepted here, but prefer leaving it out — see [Prefer Node.js over the Edge runtime](#prefer-nodejs-over-the-edge-runtime).\n\n## Common Pitfalls\n\n1. **`waitUntil` given a callback**: it takes a Promise. `waitUntil(fn())`, never `waitUntil(fn)` or `waitUntil(async () => {})` — the latter silently does nothing\n2. **Cold starts with DB connections**: use connection pooling (e.g. Neon's `@neondatabase/serverless`)\n3. **Reaching for the Edge runtime**: prefer Node.js — see [Prefer Node.js over the Edge runtime](#prefer-nodejs-over-the-edge-runtime)\n4. **Timeout exceeded**: raise `maxDuration` (800s Pro/Ent, 1800s in beta), or move to Workflow for anything longer\n5. **Bundle size**: standard limit is 250 MB uncompressed (500 MB Python). 5 GB needs the large functions beta, which existing projects must opt into with `VERCEL_SUPPORT_LARGE_FUNCTIONS=1`\n6. **Payload size**: request and response bodies cap at **4.5 MB** (`413 FUNCTION_PAYLOAD_TOO_LARGE`) — use Blob client uploads or streaming, not a bigger function\n7. **In-memory state**: Fluid shares instances across invocations and scales to zero — never keep sessions, rooms, or caches in process memory\n8. **Setting `memory` in `vercel.json`**: with Fluid Compute enabled this is not the place for it and the build warns — set it in the dashboard\n9. **Environment variables**: available in all functions automatically; use `vercel env pull` for local dev\n\n## Function Runtime Diagnostics\n\n### Timeout Diagnostics\n\n```\n504 FUNCTION_INVOCATION_TIMEOUT?\n├─ All plans default to 300s with Fluid Compute\n├─ How long does the work actually need?\n│  ├─ ≤ 300s → Already allowed on every plan; the timeout is a bug, not a limit\n│  ├─ 300–800s → Pro/Enterprise: set `maxDuration` in code or vercel.json\n│  ├─ 800–1800s → Pro/Enterprise extended-duration beta (30 min)\n│  │   ├─ Must be set PER FUNCTION — project defaults above 800s are ignored\n│  │   ├─ Runtimes: nodejs20/22/24.x, Bun 1.x/1.4.x, python3.12/3.13/3.14\n│  │   └─ Blocked if the project uses Secure Compute or Static IPs\n│  └─ > 30 min, or must survive crashes/deploys → Vercel Workflow\n├─ On Hobby? → 300s is both default AND max; no extension exists, upgrade to Pro\n├─ Client disconnected before the function finished?\n│  └─ HTTP/1.1 drops idle connections → stream heartbeat/progress data\n└─ DB query slow? → Add connection pooling, check cold start, use Global Config\n```\n\n### 500 Error Diagnostics\n\n```\n500 Internal Server Error?\n├─ Check Vercel Runtime Logs (Dashboard → Deployments → Functions tab)\n├─ Missing env vars? → Compare `.env.local` against Vercel dashboard settings\n├─ Import error? → Verify package is in `dependencies`, not `devDependencies`\n└─ Uncaught exception? → Wrap handler in try/catch, use `after()` for error reporting\n```\n\n### Invocation Failure Diagnostics\n\n```\n\"FUNCTION_INVOCATION_FAILED\"?\n├─ Memory exceeded (OOM)?\n│  ├─ Pro/Enterprise → switch to Performance (4 GB / 2 vCPU) in Settings → Functions\n│  │   └─ With Fluid compute, set it there, not in vercel.json (which warns at build)\n│  └─ Hobby → fixed at 2 GB / 1 vCPU; reduce per-request memory or upgrade\n├─ Crashed during init? → Check top-level await or heavy imports at module scope\n├─ Build failed with \"exceeded the unzipped maximum size of 250 MB\"?\n│  ├─ Trim with excludeFiles / outputFileTracingExcludes first\n│  └─ Then large functions beta: VERCEL_SUPPORT_LARGE_FUNCTIONS=1 (5 GB, Node/Bun/Python)\n├─ 413 FUNCTION_PAYLOAD_TOO_LARGE? → 4.5 MB body cap; use Blob client uploads or streaming\n└─ Container image? → Is it listening on port 80 (or $PORT)? Is it holding state between requests?\n```\n\n### Cold Start Diagnostics\n\n```\nCold start latency > 1s?\n├─ Moving to the Edge runtime is not the fix — Vercel recommends migrating off it\n├─ Fluid Compute enabled? → Reuses warm instances across concurrent invocations\n├─ Measuring in preview? → Bytecode caching is production-only; re-measure in prod\n├─ Large function bundle? → Audit imports, use dynamic imports, tree-shake\n├─ DB connection in cold start? → Use connection pooling (Neon serverless driver)\n└─ Container image? → Scales to zero after 5 min idle (30 s in preview); expect cold starts\n```\n\n### Edge Function Timeout Diagnostics\n\n```\n\"EDGE_FUNCTION_INVOCATION_TIMEOUT\"?\n├─ Edge must START the response within 25s (then may stream up to 300s)\n├─ `maxDuration` does NOT apply to the Edge runtime — there is no way to raise this\n├─ Recommended fix: drop `runtime = 'edge'` and run on Node.js\n│  └─ Node.js gives you 300s by default, 800s on Pro/Ent, 1800s in the beta\n└─ On Next.js 16.3+, `runtime = 'edge'` is unsupported — migration is required there\n```\n\n## Official Documentation\n\n- [Vercel Functions](https://vercel.com/docs/functions)\n- [Functions limits](https://vercel.com/docs/functions/limitations) — duration, memory, bundle size, large functions\n- [Configuring max duration](https://vercel.com/docs/functions/configuring-functions/duration) — including the extended 30-minute beta\n- [Configuring memory / CPU](https://vercel.com/docs/functions/configuring-functions/memory)\n- [Functions API reference](https://vercel.com/docs/functions/functions-api-reference) — `waitUntil`, `getDeadline`, SIGTERM, cancellation\n- [Fluid Compute](https://vercel.com/docs/fluid-compute)\n- [Container Images](https://vercel.com/docs/functions/container-images) — Dockerfile on Vercel\n- [Vercel Container Registry](https://vercel.com/docs/container-registry) and its [limits and pricing](https://vercel.com/docs/container-registry/limits-and-pricing)\n- [Services](https://vercel.com/docs/services) — multiple backends/frontends in one project\n- [Streaming](https://vercel.com/docs/functions/streaming-functions)\n- [WebSockets](https://vercel.com/docs/functions/websockets)\n- [Cron Jobs](https://vercel.com/docs/cron-jobs)\n- [Vercel Workflow](https://vercel.com/docs/workflows) — for anything beyond 30 minutes\n- [Edge Runtime](https://vercel.com/docs/functions/runtimes/edge) — legacy; Vercel recommends migrating to Node.js\n- [GitHub: Vercel](https://github.com/vercel/vercel)\n"},"changes":[{"path":"/description","type":"changed","before":"Vercel Functions expert guidance — Serverless Functions, Edge Functions, Fluid Compute, streaming, Cron Jobs, and runtime configuration. Use when configuring, debugging, or optimizing server-side code running on Vercel.","after":"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."},{"path":"/skill_md_contents","type":"changed","before":"---\nname: vercel-functions\ndescription: Vercel Functions expert guidance — Serverless Functions, Edge Functions, Fluid Compute, streaming, Cron Jobs, and runtime configuration. Use when configuring, debugging, or optimizing server-side code running on Vercel.\nmetadata:\n  priority: 8\n  docs:\n    - \"https://vercel.com/docs/functions\"\n    - \"https://vercel.com/docs/functions/runtimes\"\n    - \"https://vercel.com/docs/functions/websockets\"\n  sitemap: \"https://vercel.com/sitemap/docs.xml\"\n  pathPatterns:\n    - 'api/**/*.*'\n    - 'pages/api/**'\n    - 'src/pages/api/**'\n    - 'app/**/route.*'\n    - 'src/app/**/route.*'\n    - 'apps/*/api/**/*.*'\n    - 'apps/*/app/**/route.*'\n    - 'apps/*/src/app/**/route.*'\n    - 'apps/*/pages/api/**'\n    - 'vercel.json'\n    - 'apps/*/vercel.json'\n  bashPatterns:\n    - '\\bvercel\\s+dev\\b'\n    - '\\bvercel\\s+logs\\b'\n  importPatterns:\n    - 'ws'\n    - 'socket.io'\n    - 'socket.io-client'\n  promptSignals:\n    phrases:\n      - \"websocket\"\n      - \"websockets\"\n      - \"web socket\"\n      - \"socket.io\"\n      # Polling is the classic technique people reach for when they think Vercel\n      # lacks websockets. We intentionally do NOT trigger on named third-party\n      # services (Pusher, PubNub, Ably) — those are deliberate choices, not a\n      # signal that someone is working around a missing feature.\n      - \"long polling\"\n      - \"long-polling\"\n    allOf: []\n    anyOf:\n      - \"realtime\"\n      - \"bidirectional\"\n      - \"ws server\"\n      - \"polling\"\n      - \"server-sent events\"\n    noneOf: []\n    minScore: 6\n---\n\n# Vercel Functions\n\nYou are an expert in Vercel Functions — the compute layer of the Vercel platform.\n\n## Function Types\n\n### Serverless Functions (Node.js)\n- Full Node.js runtime, all npm packages available\n- Default for Next.js API routes, Server Actions, Server Components\n- Cold starts: 800ms–2.5s (with DB connections)\n- Max duration: 10s (Hobby), 300s (Pro default), 800s (Fluid Compute Pro/Enterprise)\n\n```ts\n// app/api/hello/route.ts\nexport async function GET() {\n  return Response.json({ message: 'Hello from Node.js' })\n}\n```\n\n### Edge Functions (V8 Isolates)\n- Lightweight V8 runtime, Web Standard APIs only\n- Ultra-low cold starts (<1ms globally)\n- Limited API surface (no full Node.js)\n- Best for: auth checks, redirects, A/B testing, simple transformations\n\n```ts\n// app/api/hello/route.ts\nexport const runtime = 'edge'\n\nexport async function GET() {\n  return new Response('Hello from the Edge')\n}\n```\n\n### Bun Runtime (Public Beta)\n\nAdd `\"bunVersion\": \"1.x\"` to `vercel.json` to run Node.js functions on Bun instead. ~28% lower latency for CPU-bound workloads. Supports Next.js, Express, Hono, Nitro.\n\n### Rust Runtime (Public Beta)\n\nRust functions run on Fluid Compute with HTTP streaming and Active CPU pricing. Built on the community Rust runtime. Supports environment variables up to 64 KB.\n\n### Node.js 24 LTS\n\nNode.js 24 LTS is now GA on Vercel for both builds and functions. Features V8 13.6, global `URLPattern`, Undici v7 for faster `fetch()`, and npm v11.\n\n### Choosing Runtime\n\n| Need | Runtime | Why |\n|------|---------|-----|\n| Full Node.js APIs, npm packages | `nodejs` | Full compatibility |\n| Lower latency, CPU-bound work | `nodejs` + Bun | ~28% latency reduction |\n| Ultra-low latency, simple logic | `edge` | <1ms cold start, global |\n| Database connections, heavy deps | `nodejs` | Edge lacks full Node.js |\n| Auth/redirect at the edge | `edge` | Fastest response |\n| AI streaming | Either | Both support streaming |\n| Systems-level performance | `rust` (beta) | Native speed, Fluid Compute |\n\n## Fluid Compute\n\nFluid Compute is the unified execution model for all Vercel Functions (both Node.js and Edge).\n\nKey benefits:\n- **Optimized concurrency**: Multiple invocations on a single instance — up to 85% cost reduction for high-concurrency workloads\n- **Extended durations**: Default 300s for all plans; up to 800s on Pro/Enterprise\n- **Active CPU pricing**: Charges only while CPU is actively working, not during idle/await time. Enabled by default for all plans. Memory-only periods billed at a significantly lower rate.\n- **Background processing**: `waitUntil` / `after` for post-response tasks\n- **Dynamic scaling**: Automatic during traffic spikes\n- **Bytecode caching**: Reduces cold starts via Rust-based runtime with pre-compiled function code\n- **Multi-region failover**: Default for Enterprise when Fluid is activated\n\n### Instance Sizes\n\n| Size | CPU | Memory |\n|------|-----|--------|\n| Standard (default) | 1 vCPU | 2 GB |\n| Performance | 2 vCPU | 4 GB |\n\nHobby projects use Standard CPU. The Basic CPU instance has been removed.\n\n### Background Processing with `waitUntil`\n\n```ts\n// Continue work after sending response\nimport { waitUntil } from '@vercel/functions'\n\nexport async function POST(req: Request) {\n  const data = await req.json()\n\n  // Send response immediately\n  const response = Response.json({ received: true })\n\n  // Continue processing in background\n  waitUntil(async () => {\n    await processAnalytics(data)\n    await sendNotification(data)\n  })\n\n  return response\n}\n```\n\n### Next.js `after` (equivalent)\n\n```ts\nimport { after } from 'next/server'\n\nexport async function POST(req: Request) {\n  const data = await req.json()\n\n  after(async () => {\n    await logToAnalytics(data)\n  })\n\n  return Response.json({ ok: true })\n}\n```\n\n## Streaming\n\nZero-config streaming on **both runtimes**, including Server-Sent Events (SSE). Essential for AI applications.\n\n> **You do NOT need `runtime = 'edge'` for streaming or SSE.** Streaming responses (`ReadableStream`, `text/event-stream`) work on the default Node.js runtime — this is a common misconception. Stay on Node.js (Fluid Compute) so you keep full Node.js APIs, npm packages, and longer durations; Edge offers no streaming advantage here.\n\n```ts\nexport async function POST(req: Request) {\n  const encoder = new TextEncoder()\n  const stream = new ReadableStream({\n    async start(controller) {\n      for (const chunk of data) {\n        controller.enqueue(encoder.encode(chunk))\n        await new Promise(r => setTimeout(r, 100))\n      }\n      controller.close()\n    },\n  })\n\n  return new Response(stream, {\n    headers: { 'Content-Type': 'text/event-stream' },\n  })\n}\n```\n\nFor AI streaming, use the AI SDK's `toUIMessageStreamResponse()` (for chat UIs with `useChat`) which handles SSE formatting automatically.\n\n## WebSockets\n\nVercel Functions can hold open bidirectional WebSocket connections — use them for realtime features like interactive AI streaming, chat, and collaborative apps. There is **no separate WebSocket-server product and no third-party service (Pusher, Ably, etc.) required** — it runs on Vercel Functions directly. Requires **Fluid Compute**, which is the default for new projects.\n\n**How it works**: a WebSocket starts as an HTTP `GET` with an `Upgrade` header, so it passes through the same Routing Middleware, rewrites, Firewall rules, and rate limits as any other request. After the upgrade, the connection is pinned to a single function instance for its lifetime; Fluid Compute lets one instance serve many concurrent connections. Active CPU pricing means you're billed while processing messages, not for idle open connections — the same limits and pricing as other Function invocations apply.\n\n### `ws` (no extra config)\n\nWebSockets work like any distributed WebSocket server — export an `http.Server` and use a library such as `ws`:\n\n```ts\n// api/ws.ts\nimport http from 'http'\nimport { WebSocketServer } from 'ws'\n\nconst server = http.createServer()\nconst wss = new WebSocketServer({ server })\n\nwss.on('connection', (ws) => {\n  ws.on('message', (data) => ws.send(data)) // echo\n})\n\nexport default server\n```\n\n### Socket.IO\n\nHigher-level realtime libraries like Socket.IO work too. Configure the **client** to use the WebSocket transport directly — Socket.IO defaults to HTTP long-polling, which won't work:\n\n```ts\n// api/socket-io.ts\nimport http from 'http'\nimport { Server } from 'socket.io'\n\nconst server = http.createServer()\nconst io = new Server(server)\n\nio.on('connection', (socket) => {\n  socket.on('message', (data) => socket.send(data))\n})\n\nexport default server\n```\n\n```ts\n// client.ts\nimport { io } from 'socket.io-client'\n\nconst socket = io('https://your-domain.com', {\n  // Socket.IO appends /socket.io, so the full path becomes /api/socket-io/socket.io\n  path: '/api/socket-io/socket.io',\n  transports: ['websocket'], // required — Socket.IO defaults to HTTP long-polling\n})\n```\n\nExpress, Hono, and Nitro (including Nuxt, via native WebSocket support) serve WebSockets the same way — export the HTTP server. Python frameworks work too: FastAPI handles the upgrade natively, and `python-socketio` is protocol-compatible with the JS Socket.IO client.\n\n### Next.js\n\nNext.js doesn't expose an API for handling WebSocket upgrades. Use `experimental_upgradeWebSocket()` from `@vercel/functions` inside a route handler:\n\n```ts\n// app/api/ws/route.ts\nimport { experimental_upgradeWebSocket, type WebSocketData } from '@vercel/functions'\n\nexport async function GET() {\n  return experimental_upgradeWebSocket((ws) => {\n    ws.on('message', (data: WebSocketData) => ws.send(data))\n  })\n}\n```\n\n### Reconnects and persistent state\n\n- **Connections close when the function reaches its max duration.** Clients must reconnect with backoff, then resubscribe to channels and reload any state they need.\n- **No instance affinity across connections.** A reconnect — or a new deployment — may land on a different instance, so never keep durable state, presence, rooms, or pub/sub coordination in memory. Use an external store such as [Redis from the Marketplace](https://vercel.com/marketplace/redis).\n\n```ts\n// client.ts — reconnect with exponential backoff\nlet socket: WebSocket\nlet delay = 1000\n\nfunction connect() {\n  socket = new WebSocket('wss://your-domain.com/api/ws')\n  socket.addEventListener('open', () => { delay = 1000 })\n  socket.addEventListener('message', (e) => console.log(e.data))\n  socket.addEventListener('close', () => {\n    setTimeout(connect, delay)\n    delay = Math.min(delay * 2, 30000)\n  })\n}\n\nconnect()\n```\n\n## Cron Jobs\n\nSchedule function invocations via `vercel.json`:\n\n```json\n{\n  \"crons\": [\n    {\n      \"path\": \"/api/daily-report\",\n      \"schedule\": \"0 8 * * *\"\n    },\n    {\n      \"path\": \"/api/cleanup\",\n      \"schedule\": \"0 */6 * * *\"\n    }\n  ]\n}\n```\n\nThe cron endpoint receives a normal HTTP request. Verify it's from Vercel:\n\n```ts\nexport async function GET(req: Request) {\n  const authHeader = req.headers.get('authorization')\n  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {\n    return new Response('Unauthorized', { status: 401 })\n  }\n  // Do scheduled work\n  return Response.json({ ok: true })\n}\n```\n\n## Configuration via vercel.json\n\n**Deprecation notice**: Support for the legacy `now.json` config file will be removed on **March 31, 2026**. Rename `now.json` to `vercel.json` (no content changes required).\n\n```json\n{\n  \"functions\": {\n    \"app/api/heavy/**\": {\n      \"maxDuration\": 300,\n      \"memory\": 1024\n    },\n    \"app/api/edge/**\": {\n      \"runtime\": \"edge\"\n    }\n  }\n}\n```\n\n## Timeout Limits\n\nAll plans now default to 300s execution time with Fluid Compute.\n\n| Plan | Default | Max |\n|------|---------|-----|\n| Hobby | 300s | 300s |\n| Pro | 300s | 800s |\n| Enterprise | 300s | 800s |\n\n## Common Pitfalls\n\n1. **Cold starts with DB connections**: Use connection pooling (e.g., Neon's `@neondatabase/serverless`)\n2. **Edge limitations**: No `fs`, no native modules, limited `crypto` — use Node.js runtime if needed\n3. **Timeout exceeded**: Use Fluid Compute for long-running tasks, or Workflow DevKit for very long processes\n4. **Bundle size**: Functions support up to 5 GB package size on Fluid Compute (up from 250 MB); request bodies up to 100 MB (up from 4.5 MB)\n5. **Environment variables**: Available in all functions automatically; use `vercel env pull` for local dev\n\n## Function Runtime Diagnostics\n\n### Timeout Diagnostics\n\n```\n504 Gateway Timeout?\n├─ All plans default to 300s with Fluid Compute\n├─ Pro/Enterprise: configurable up to 800s\n├─ Long-running task?\n│  ├─ Under 5 min → Use Fluid Compute with streaming\n│  ├─ Up to 15 min → Use Vercel Functions with `maxDuration` in vercel.json\n│  └─ Hours/days → Use Workflow DevKit (DurableAgent or workflow steps)\n└─ DB query slow? → Add connection pooling, check cold start, use Edge Config\n```\n\n### 500 Error Diagnostics\n\n```\n500 Internal Server Error?\n├─ Check Vercel Runtime Logs (Dashboard → Deployments → Functions tab)\n├─ Missing env vars? → Compare `.env.local` against Vercel dashboard settings\n├─ Import error? → Verify package is in `dependencies`, not `devDependencies`\n└─ Uncaught exception? → Wrap handler in try/catch, use `after()` for error reporting\n```\n\n### Invocation Failure Diagnostics\n\n```\n\"FUNCTION_INVOCATION_FAILED\"?\n├─ Memory exceeded? → Increase `memory` in vercel.json (up to 3008 MB on Pro)\n├─ Crashed during init? → Check top-level await or heavy imports at module scope\n└─ Edge Function crash? → Check for Node.js APIs not available in Edge runtime\n```\n\n### Cold Start Diagnostics\n\n```\nCold start latency > 1s?\n├─ Using Node.js runtime? → Consider Edge Functions for latency-sensitive routes\n├─ Large function bundle? → Audit imports, use dynamic imports, tree-shake\n├─ DB connection in cold start? → Use connection pooling (Neon serverless driver)\n└─ Enable Fluid Compute to reuse warm instances across requests\n```\n\n### Edge Function Timeout Diagnostics\n\n```\n\"EDGE_FUNCTION_INVOCATION_TIMEOUT\"?\n├─ Edge Functions have 25s hard limit (not configurable)\n├─ Move heavy computation to Node.js Serverless Functions\n└─ Use streaming to start response early, process in background with `waitUntil`\n```\n\n## Official Documentation\n\n- [Vercel Functions](https://vercel.com/docs/functions)\n- [Serverless Functions](https://vercel.com/docs/functions)\n- [Edge Functions](https://vercel.com/docs/functions)\n- [Fluid Compute](https://vercel.com/docs/fluid-compute)\n- [Streaming](https://vercel.com/docs/functions/streaming)\n- [WebSockets](https://vercel.com/docs/functions/websockets)\n- [Cron Jobs](https://vercel.com/docs/cron-jobs)\n- [GitHub: Vercel](https://github.com/vercel/vercel)\n","after":"---\nname: vercel-functions\ndescription: 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.\nsummary: \"Vercel Functions run on Fluid Compute with Node.js as the default runtime — strongly prefer it over `runtime = 'edge'` (Vercel recommends migrating off Edge, and Next.js 16.3+ no longer supports it). Duration: 300s default on every plan including Hobby, 800s max on Pro/Enterprise, 1800s (30 min) per-function in the extended beta; beyond that use Vercel Workflow. Bundles: 250 MB standard (500 MB Python), 5 GB via the large functions beta (`VERCEL_SUPPORT_LARGE_FUNCTIONS=1`). Request/response bodies cap at 4.5 MB. Memory is dashboard-only (Standard 2 GB/1 vCPU, Performance 4 GB/2 vCPU; Hobby fixed). Docker works: add `Dockerfile.vercel` to run an OCI image as an autoscaling, stateless, scale-to-zero Function.\"\nmetadata:\n  priority: 8\n  docs:\n    - \"https://vercel.com/docs/functions\"\n    - \"https://vercel.com/docs/functions/runtimes\"\n    - \"https://vercel.com/docs/functions/limitations\"\n    - \"https://vercel.com/docs/functions/configuring-functions/duration\"\n    - \"https://vercel.com/docs/functions/container-images\"\n    - \"https://vercel.com/docs/fluid-compute\"\n    - \"https://vercel.com/docs/functions/websockets\"\n  sitemap: \"https://vercel.com/sitemap.xml\"\n  pathPatterns:\n    - 'api/**/*.*'\n    - 'pages/api/**'\n    - 'src/pages/api/**'\n    - 'app/**/route.*'\n    - 'src/app/**/route.*'\n    - 'apps/*/api/**/*.*'\n    - 'apps/*/app/**/route.*'\n    - 'apps/*/src/app/**/route.*'\n    - 'apps/*/pages/api/**'\n    - 'vercel.json'\n    - 'apps/*/vercel.json'\n    - 'vercel.ts'\n    - 'apps/*/vercel.ts'\n    # Vercel builds `Dockerfile.vercel` / `Containerfile.vercel` into a\n    # container-image Function, so these are Functions config, not generic Docker.\n    - 'Dockerfile.vercel'\n    - '*/Dockerfile.vercel'\n    - 'Containerfile.vercel'\n    - '*/Containerfile.vercel'\n  bashPatterns:\n    - '\\bvercel\\s+dev\\b'\n    - '\\bvercel\\s+logs\\b'\n    - '\\bvercel\\s+vcr\\b'\n  importPatterns:\n    - 'ws'\n    - 'socket.io'\n    - 'socket.io-client'\n  promptSignals:\n    phrases:\n      - \"websocket\"\n      - \"websockets\"\n      - \"web socket\"\n      - \"socket.io\"\n      # Polling is the classic technique people reach for when they think Vercel\n      # lacks websockets. We intentionally do NOT trigger on named third-party\n      # services (Pusher, PubNub, Ably) — those are deliberate choices, not a\n      # signal that someone is working around a missing feature.\n      - \"long polling\"\n      - \"long-polling\"\n      # Duration: people hit the ceiling and ask about it in these words.\n      - \"maxduration\"\n      - \"max duration\"\n      - \"function timeout\"\n      - \"function times out\"\n      - \"long-running function\"\n      - \"long running function\"\n      - \"504\"\n      # Bundle size: the 250 MB error message is the usual entry point.\n      - \"unzipped maximum size\"\n      - \"250mb\"\n      - \"250 mb\"\n      - \"large function\"\n      - \"bundle size limit\"\n      # Containers: Dockerfile.vercel is a Functions feature, not just packaging.\n      - \"dockerfile\"\n      - \"container image\"\n      - \"container registry\"\n      - \"runtime edge\"\n      - \"edge runtime\"\n    allOf:\n      - [\"docker\", \"vercel\"]\n      - [\"hobby\", \"limit\"]\n    anyOf:\n      - \"realtime\"\n      - \"bidirectional\"\n      - \"ws server\"\n      - \"polling\"\n      - \"server-sent events\"\n      - \"fluid compute\"\n      - \"active cpu\"\n    noneOf: []\n    minScore: 6\nvalidate:\n  -\n    pattern: export\\s+default\\s+function\n    message: 'Use named exports (GET, POST, PUT, DELETE) instead of default export for route handlers'\n    severity: error\n    # Skip on App Router page / layout / loading / error / not-found / sitemap / template / default files,\n    # which require a default export by Next.js convention. Detected via the 'use client' directive,\n    # an App Router config export (metadata, dynamic, revalidate, fetchCache, runtime), an `export default\n    # function` whose name matches an App Router file (Page / Layout / Loading / etc.), or any JSX\n    # element with a capitalised component tag — all signals that the file is a page-style file rather\n    # than a route handler. See anthropics/claude-code#54989.\n    skipIfFileContains: \"(?:^|\\\\n)\\\\s*['\\\"]use\\\\s+client['\\\"]|export\\\\s+const\\\\s+(?:metadata|dynamic|revalidate|fetchCache|runtime)\\\\b|export\\\\s+default\\\\s+(?:async\\\\s+)?function\\\\s+\\\\w*(?:Page|Layout|Loading|Error|NotFound|Sitemap|Template|Default|sitemap|robots|opengraph|manifest)\\\\b|<[A-Z][A-Za-z0-9]*|\\\\{\\\\s*children\\\\s*[,}:]|MetadataRoute\\\\.|from\\\\s+['\\\"]next/(?:font|image|link|navigation|headers|cookies)['\\\"]\"\n  -\n    pattern: NextApiRequest|NextApiResponse\n    message: 'NextApiRequest/NextApiResponse are Pages Router types — use Web API Request/Response'\n    severity: error\n  -\n    # Vercel's docs recommend migrating off the Edge runtime, and Next.js 16.3+\n    # doesn't support it. Surfaced as a recommendation rather than an error:\n    # existing Edge functions still work, so this is a nudge, not a blocker.\n    # Matches `\"runtime\": \"edge\"` in vercel.json too.\n    pattern: 'runtime[''\"]?\\s*[=:]\\s*[''\"]edge[''\"]'\n    message: 'Consider dropping `runtime = \"edge\"`. Vercel recommends migrating from Edge to Node.js, and Next.js 16.3+ no longer supports it. Node.js on Fluid Compute runs in the same regions at the same price with full Node.js APIs, longer durations, and larger bundles.'\n    severity: recommended\n  -\n    pattern: 'from\\s+[''\"](openai|@anthropic-ai/sdk|anthropic)[''\"]|new\\s+(OpenAI|Anthropic)\\('\n    message: 'Direct AI provider SDK detected in route handler. Use the Vercel AI SDK for streaming, tools, and provider abstraction.'\n    severity: recommended\n    upgradeToSkill: ai-sdk\n    upgradeWhy: 'Replace vendor-locked provider SDKs with @ai-sdk/openai or @ai-sdk/anthropic for unified streaming and tool support.'\n    skipIfFileContains: '@ai-sdk/|from\\s+[''\"](ai)[''\"]|import.*from\\s+[''\"](ai)[''\"]|streamText|generateText'\n  -\n    pattern: 'setTimeout\\s*\\(|setInterval\\s*\\(|await\\s+new\\s+Promise\\s*\\([^)]*setTimeout'\n    message: 'Long-running or polling logic detected in a serverless handler. Functions have execution time limits.'\n    severity: recommended\n    upgradeToSkill: workflow\n    upgradeWhy: 'Move delayed/polling logic to Vercel Workflow for durable execution with pause, resume, retries, and crash safety.'\n    skipIfFileContains: 'use workflow|use step'\n  -\n    pattern: 'writeFile(Sync)?\\(|createWriteStream\\(|from\\s+[''\"](multer|formidable)[''\"]|fs\\.writeFile'\n    message: 'Local filesystem write detected. Serverless functions have ephemeral, read-only filesystems.'\n    severity: error\n    upgradeToSkill: vercel-storage\n    upgradeWhy: 'Replace local filesystem writes with Vercel Blob, Neon, or Upstash for persistent, platform-native storage.'\n    skipIfFileContains: '@vercel/blob|@upstash/|@neondatabase/'\n  -\n    pattern: 'export\\s+(async\\s+)?function\\s+(GET|POST|PUT|PATCH|DELETE)\\b'\n    message: 'Route handler has no observability instrumentation. Add logging and error tracking for production debugging.'\n    severity: warn\n    skipIfFileContains: 'console\\.error|logger\\.|captureException|Sentry|@vercel/otel|withTracing'\n  -\n    pattern: 'from\\s+[''\"\"](lru-cache|node-cache|memory-cache)[''\"\"]|new\\s+(LRUCache|NodeCache|Map)\\(\\s*\\).*cache'\n    message: 'In-process memory cache detected in serverless function. Process memory is not shared across invocations.'\n    severity: recommended\n    upgradeToSkill: runtime-cache\n    upgradeWhy: 'Replace in-process caches with Vercel Runtime Cache (getCache from @vercel/functions) for region-aware caching that persists across invocations.'\n    skipIfFileContains: 'getCache|from\\s+[''\"\"]\\@vercel/functions[''\"\"]'\n  -\n    pattern: 'maxRetries\\s*[=:]|retryCount\\s*[=:]|retry\\s*\\(\\s*|for\\s*\\([^)]*retry|while\\s*\\([^)]*retry'\n    message: 'Manual retry logic detected. Use Vercel Workflow SDK for automatic retries with durable execution.'\n    severity: recommended\n    upgradeToSkill: workflow\n    upgradeWhy: 'Replace manual retry loops with Workflow SDK steps that provide automatic retries, crash safety, and observability.'\n    skipIfFileContains: 'use workflow|use step|from\\s+[''\"\"](workflow)[''\"\"]'\n  -\n    pattern: 'from\\s+[''\"](express)[''\"\"]|require\\s*\\(\\s*[''\"](express)[''\"\"\\)]'\n    message: 'Express.js detected in a Vercel project. Vercel Functions use the Web Request/Response API — Express middleware, req/res, and app.listen() do not work in serverless.'\n    severity: recommended\n    upgradeToSkill: vercel-functions\n    upgradeWhy: 'Replace Express with Next.js route handlers (export async function GET/POST) or Vercel Functions using the Web Request/Response API.'\n    skipIfFileContains: 'export\\s+(async\\s+)?function\\s+(GET|POST|PUT|PATCH|DELETE)|from\\s+[''\"\"](next/server|@vercel/functions)[''\"\"]'\nretrieval:\n  aliases:\n    - serverless functions\n    - api routes\n    - edge functions\n    - lambda\n    - websockets\n    - socket.io\n    - docker\n    - dockerfile\n    - container images\n    - function timeout\n    - max duration\n    - bundle size\n    - hobby limits\n  intents:\n    - create serverless function\n    - configure function runtime\n    - optimize cold starts\n    - add api route\n    - serve a websocket connection\n    - run a function for longer than 5 minutes\n    - deploy a dockerfile\n    - fix a function that exceeds the bundle size limit\n    - check plan limits for functions\n  entities:\n    - Serverless Functions\n    - Edge Functions\n    - Fluid Compute\n    - Long-duration functions\n    - Large functions\n    - Container Images\n    - Vercel Container Registry\n    - streaming\n    - WebSockets\n    - Cron Jobs\nchainTo:\n  -\n    pattern: 'from\\s+[''\\\"](openai|@anthropic-ai/sdk|anthropic)[''\"]|new\\s+(OpenAI|Anthropic)\\('\n    targetSkill: ai-sdk\n    message: 'Direct AI provider SDK in route handler — loading AI SDK guidance for unified streaming and tool support.'\n  -\n    pattern: 'setTimeout\\s*\\(|setInterval\\s*\\(|await\\s+new\\s+Promise\\s*\\([^)]*setTimeout'\n    targetSkill: workflow\n    message: 'Long-running or polling logic in serverless handler — loading Workflow SDK for durable execution.'\n  -\n    pattern: 'writeFile(Sync)?\\(|createWriteStream\\(|from\\s+[''\\\"](multer|formidable)[''\"]|fs\\.writeFile'\n    targetSkill: vercel-storage\n    message: 'Local filesystem write in serverless function — loading Vercel Storage guidance for platform-native persistence.'\n  -\n    pattern: 'from\\s+[''\"\"]@vercel/(postgres|kv)[''\"\"]'\n    targetSkill: vercel-storage\n    message: '@vercel/postgres and @vercel/kv are sunset — loading Vercel Storage guidance for Neon and Upstash migration.'\n  -\n    pattern: 'generateObject\\s*\\(|streamObject\\s*\\(|toDataStreamResponse|maxSteps\\b|CoreMessage\\b'\n    targetSkill: ai-sdk\n    message: 'Deprecated AI SDK v5 API detected — loading AI SDK guidance for migration.'\n  -\n    pattern: 'while\\s*\\(\\s*true\\s*\\)\\s*\\{|for\\s*\\(\\s*;\\s*;\\s*\\)\\s*\\{|setInterval\\s*\\(\\s*async'\n    targetSkill: workflow\n    message: 'Polling loop in serverless function detected — loading Workflow SDK for durable, crash-safe execution with pause/resume.'\n    skipIfFileContains: \"use workflow|use step|from\\\\s+['\\\"]workflow['\\\"]\"\n  -\n    pattern: \"from\\\\s+['\\\"]express['\\\"]|require\\\\s*\\\\(\\\\s*['\\\"]express['\\\"]\"\n    targetSkill: vercel-functions\n    message: 'Express.js detected — loading Vercel Functions guidance for Web Request/Response API route handlers that replace Express middleware and routing.'\n    skipIfFileContains: \"export\\\\s+(async\\\\s+)?function\\\\s+(GET|POST|PUT|PATCH|DELETE)\"\n  -\n    pattern: 'from\\s+[''\"\"](lru-cache|node-cache|memory-cache)[''\"\"]|new\\s+(LRUCache|NodeCache|Map)\\(\\s*\\).*cache'\n    targetSkill: runtime-cache\n    message: 'In-process memory cache in serverless function — loading Runtime Cache guidance for region-aware caching that persists across invocations.'\n    skipIfFileContains: 'getCache|from\\s+[''\"\"]\\@vercel/functions[''\"\"]'\n  -\n    pattern: 'maxRetries\\s*[=:]|retryCount\\s*[=:]|retry\\s*\\(\\s*|for\\s*\\([^)]*retry|while\\s*\\([^)]*retry'\n    targetSkill: workflow\n    message: 'Manual retry logic in serverless handler — loading Workflow SDK guidance for automatic retries with durable execution.'\n    skipIfFileContains: 'use workflow|use step|from\\s+[''\"\"](workflow)[''\"\"]'\n\n  -\n    pattern: 'import\\s*\\{[^}]*\\bmetric\\b[^}]*\\}\\s*from\\s*[''\"]@vercel/functions[''\"]'\n    targetSkill: custom-metrics\n    message: '@vercel/functions metric() detected — loading Custom Metrics guidance for naming, attributes, and vc metrics queries.'\n\n---\n\n# Vercel Functions\n\nYou are an expert in Vercel Functions — the compute layer of the Vercel platform.\n\n## Prefer Node.js over the Edge runtime\n\n**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.**\n\nThis 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.\n\nEverywhere 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.\n\n### The default to reach for\n\n```ts\n// app/api/hello/route.ts — no runtime export needed.\nexport async function GET() {\n  return Response.json({ message: 'Hello from Node.js on Fluid Compute' })\n}\n```\n\nNode.js is the default. Omit `export const runtime` entirely rather than writing `export const runtime = 'nodejs'`.\n\n### Reasons people reach for Edge — and what to do instead\n\n| \"I need Edge because…\" | Reality | Do this instead |\n|---|---|---|\n| \"…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 |\n| \"…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 |\n| \"…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) |\n| \"…it's cheaper\" | Identical Active CPU pricing | Stay on Node.js |\n| \"…it has faster cold starts\" | Fluid Compute reuses warm instances across concurrent invocations and bytecode-caches Node 20+ in production | Stay on Node.js |\n| \"…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 |\n\n### What Edge actually costs you\n\n- No `fs`, no native modules, no `require()` — ESM only, and most npm packages with Node.js dependencies simply will not load\n- No `eval` / `new Function` / dynamic `WebAssembly.instantiate`\n- **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\n- 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\n- No long-duration or large-function support of any kind\n\n### Migrating an existing Edge function\n\nWorth 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.\n\n1. Remove `export const runtime = 'edge'` (or `runtime: 'edge'` in `vercel.json` / the `config` object).\n2. Replace `next/server` Edge-only imports where applicable; the Web `Request`/`Response` handler signature is unchanged, so most route handlers need no other edit.\n3. If you pinned execution with the Edge-only `preferredRegion`, use `regions` in `vercel.json` instead.\n4. Confirm Fluid Compute is on (default since April 23, 2025) and redeploy.\n\nThere is no rollback story to plan for: Node.js is a superset of what the function could do on Edge.\n\n## Function Types\n\n### Node.js (the default)\n- Full Node.js runtime, all npm packages available\n- Default for Next.js route handlers, Server Actions, Server Components, and any file in `/api`\n- **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`\n- Duration: 300s default on every plan; 800s max on Pro/Enterprise; 1800s with the extended-duration beta\n\n### Bun\nAdd `\"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.\n\n### Python\nPython 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.\n\n### Rust\nRust 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.\n\n### Container images (Docker)\nAny OCI image via `Dockerfile.vercel`. See [Docker and Container Images](#docker-and-container-images) below.\n\n### Edge (legacy — not recommended)\nV8 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).\n\n### Choosing a Runtime\n\n| Need | Runtime | Why |\n|------|---------|-----|\n| Anything not listed below | `nodejs` | The default, and correct nearly always |\n| Full Node.js APIs, npm packages | `nodejs` | Full compatibility |\n| AI streaming, SSE, WebSockets | `nodejs` | Zero-config streaming, long durations |\n| Lower latency, CPU-bound work | `nodejs` + Bun | ~28% latency reduction |\n| Database connections, heavy deps | `nodejs` | Pin `regions` next to the database |\n| Data/ML libraries, big model files | `nodejs` or `python` + large functions | Up to 5 GB bundles |\n| Systems-level performance | `rust` | Native speed on Fluid Compute |\n| Custom system libraries (FFmpeg, Chromium), Go/Ruby/PHP, unsupported frameworks | container image | Bring your own Dockerfile |\n| Auth, redirects, A/B tests before the cache | Routing Middleware | Runs on Node.js, framework-agnostic |\n| Hours-to-months of execution | Vercel Workflow | Durable steps, no duration limit |\n\n`edge` is deliberately absent: there's no row here where it's the better answer for new code.\n\n## Fluid Compute\n\nFluid 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.\n\nLong-duration, large-function, and container-image support all depend on it.\n\nKey behaviors:\n- **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.\n- **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.\n- **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.\n- **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.\n- **Cross-AZ and cross-region failover**: fails over to another availability zone in-region first, then to the next closest region.\n- **Graceful shutdown**: `SIGTERM` before termination (see below).\n\n### Instance Sizes (memory / CPU)\n\n| Type | Memory / CPU | Use |\n|------|--------------|-----|\n| Standard (default) | 2 GB / 1 vCPU | Predictable performance for production workloads |\n| Performance | 4 GB / 2 vCPU | Latency-sensitive applications and SSR workloads |\n\n- **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.)\n- **Pro/Enterprise only.** Hobby always runs Standard (2 GB / 1 vCPU) and cannot configure it. The Basic instance has been removed.\n- 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.\n- 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.\n\n### Settings precedence\n\nFunction code (`export const maxDuration`) → `vercel.json` → dashboard → Fluid defaults. Later entries lose.\n\n### Background Processing with `waitUntil`\n\n`waitUntil` takes a **Promise**, not a callback. Passing a function does nothing — a common and silent bug.\n\n```ts\nimport { waitUntil } from '@vercel/functions'\n\nexport async function POST(req: Request) {\n  const data = await req.json()\n\n  // Correct: invoke the async work and hand over the promise.\n  waitUntil(processAnalytics(data))\n\n  // For several tasks, combine them:\n  waitUntil(Promise.all([sendNotification(data), updateCache(data)]))\n\n  return Response.json({ received: true })\n}\n```\n\n### Next.js `after` (equivalent)\n\n```ts\nimport { after } from 'next/server'\n\nexport async function POST(req: Request) {\n  const data = await req.json()\n\n  after(async () => {\n    await logToAnalytics(data)\n  })\n\n  return Response.json({ ok: true })\n}\n```\n\n### Graceful shutdown and request cancellation\n\n```ts\n// Runs on scale-down. 500 ms to clean up (30 s for container images).\nprocess.on('SIGTERM', () => {\n  // flush buffers, close pools\n})\n```\n\nRequest 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.\n\n```json filename=\"vercel.json\"\n{\n  \"functions\": {\n    \"api/*\": { \"supportsCancellation\": true }\n  }\n}\n```\n\n```ts\nexport async function GET(request: Request) {\n  // Pass the signal through so upstream work stops too.\n  const res = await fetch('https://upstream.example.com', { signal: request.signal })\n  return new Response(res.body, { status: res.status })\n}\n```\n\n## Duration and Long-Duration Functions\n\n### Duration limits\n\nWith Fluid Compute (default), per [Vercel's limits](https://vercel.com/docs/functions/limitations#max-duration):\n\n| Plan | Default | Maximum | Extended maximum |\n|------|---------|---------|------------------|\n| Hobby | 300s (5 min) | 300s (5 min) | — |\n| Pro | 300s (5 min) | 800s | 1800s (30 min) — Beta |\n| Enterprise | 300s (5 min) | 800s | 1800s (30 min) — Beta |\n\nThe 800s maximum is **generally available** on Pro and Enterprise. The 1800s extended maximum is **in beta**. Exceeding the limit returns `504 FUNCTION_INVOCATION_TIMEOUT`.\n\n**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.\n\n### Setting `maxDuration`\n\n```ts\n// app/api/report/route.ts — Next.js App Router (and Node.js, SvelteKit, Astro,\n// Nuxt, Remix via their own config). Value is in seconds.\nexport const maxDuration = 800\n\nexport async function POST(request: Request) {\n  return Response.json({ ok: true })\n}\n```\n\nFor other frameworks and runtimes — Next.js < 13.5, Rust, Go, Python, Ruby — use `vercel.json`:\n\n```json\n{\n  \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n  \"functions\": {\n    \"api/long-task.py\": { \"maxDuration\": 1800 }\n  }\n}\n```\n\nGlob 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.\n\nTo change the project-wide default: Settings → Functions → **Function Max Duration**.\n\n### Extended max duration (30 minutes) — Beta\n\nPro and Enterprise teams can run individual functions for up to **1800s**. Requirements, all of which are load-bearing:\n\n- **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.\n- **Supported runtimes only**: `nodejs20.x`, `nodejs22.x`, `nodejs24.x`, Bun `1.x` and `1.4.x`, `python3.12`, `python3.13`, `python3.14`.\n- **Fluid Compute must be enabled** (default for new projects).\n- **Secure Compute and Static IPs do not support durations above 800s** during the beta. If the project uses either, you are capped at 800s.\n\n```ts\n// app/api/long-task/route.ts\nexport const maxDuration = 1800 // 30 minutes\n\nexport async function POST(request: Request) {\n  await doTheLongThing()\n  return Response.json({ ok: true })\n}\n```\n\n### Keeping a long request alive\n\nOver 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.\n\nUse `getDeadline()` to find out how much time is actually left and bail out cleanly:\n\n```ts\nimport { getDeadline } from '@vercel/functions'\n\nconst deadline = getDeadline() // Date | undefined (undefined outside the Vercel Functions runtime)\nconst msRemaining = deadline ? deadline.getTime() - Date.now() : Infinity\n```\n\n### Cost of long functions\n\nActive 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.\n\n### When 30 minutes is not enough\n\nDo 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:\n\n- ≤ 300s → any plan, no configuration needed\n- 300–800s → Pro/Enterprise, set `maxDuration`\n- 800–1800s → Pro/Enterprise, extended-duration beta, per-function config\n- Beyond 30 min, or needs to survive a crash/deploy → Vercel Workflow (`workflow` skill)\n\nWorkflow steps themselves support extended function durations, so a single step can also run up to 30 minutes.\n\n## Large Functions (bundle size)\n\n### Standard limits\n\n| Runtime | Uncompressed bundle limit |\n|---------|---------------------------|\n| Node.js, Bun, Rust, Go | 250 MB (includes runtime layers) |\n| Python | 500 MB |\n| Edge runtime | 1 MB Hobby / 2 MB Pro / 4 MB Enterprise, **after gzip** |\n\nBlowing the limit fails the build with `Serverless Function has exceeded the unzipped maximum size of 250 MB`.\n\n### Large functions — Beta\n\nLarge 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.\n\n- **Runtimes**: Node.js, Bun, Python.\n- **Requires Fluid Compute with Active CPU** enabled (default for new projects).\n- **New projects are eligible by default.** Existing projects opt in with the `VERCEL_SUPPORT_LARGE_FUNCTIONS` environment variable, then redeploy:\n\n```bash\nvercel env add VERCEL_SUPPORT_LARGE_FUNCTIONS   # value: 1  (use 0 to disable)\n```\n\nThe environment variable always takes precedence over the project default, in both directions.\n\n- **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.\n- **Not supported with Secure Compute or Static IPs.**\n\n### Shrinking a bundle first\n\nA 5 GB function still costs you cold-start time. Trim before you opt in:\n\nIn `vercel.json` (not supported in Next.js — see below):\n\n```json filename=\"vercel.json\"\n{\n  \"functions\": {\n    \"api/**/*.py\": {\n      \"excludeFiles\": \"{tests/**,__tests__/**,**/*.test.py,fixtures/**,testdata/**}\"\n    }\n  }\n}\n```\n\n- Next.js ignores `includeFiles`/`excludeFiles` — use `outputFileTracingIncludes` / `outputFileTracingExcludes` in `next.config.js` instead.\n- Audit heavy imports, prefer dynamic `import()`, and check for a package in `dependencies` that belongs in `devDependencies`.\n\n### Request and response payloads\n\nBundle size is not payload size. The **request or response body of a Function is capped at 4.5 MB**; exceeding it returns `413 FUNCTION_PAYLOAD_TOO_LARGE`. For larger data:\n\n- **Uploads** → Vercel Blob **client uploads**, which send the file browser → Blob directly, bypassing the function\n- **Large responses** → stream them; streamed responses are not subject to the limit\n- Otherwise, chunk across multiple requests\n\n## Docker and Container Images\n\nVercel Functions run **OCI-compatible container images**. This is first-class Docker support: bring a Dockerfile, get an autoscaling function with scale-to-zero and Active CPU pricing. It is *not* a VM or a long-lived server.\n\n### Quick start\n\nCreate `Dockerfile.vercel` (or `Containerfile.vercel`) at the project root. Vercel detects it automatically and adds a rewrite routing all traffic to the image.\n\n```docker\n# Dockerfile.vercel\nFROM node:26-alpine\n\nRUN npm i -g srvx\nWORKDIR /app\nCOPY server.ts .\n\n# srvx listens on $PORT by default\nCMD [\"srvx\", \"--prod\"]\n```\n\n```ts\n// server.ts\nexport default {\n  fetch(req: Request) {\n    return Response.json({ ip: req.headers.get('x-forwarded-for') })\n  },\n}\n```\n\nDeploy with `vercel deploy` or a Git push. During the build, the image is built and pushed to [Vercel Container Registry (VCR)](https://vercel.com/docs/container-registry).\n\n### The rules that actually bite\n\n- **Serve HTTP on port 80**, or override with the `PORT` environment variable in project settings. A container that doesn't listen gets no traffic.\n- **Containers must be stateless.** Each instance takes a request, returns a response, and keeps nothing between calls — that is what allows autoscaling and scale-to-zero. Persist to a Marketplace database, Redis, or Blob; never to the container filesystem.\n- **Scale to zero** after 5 minutes without traffic in production, 30 seconds in preview. Cold starts are real; do not assume a warm process.\n- **`SIGTERM` with a 30-second grace period** on scale-down (regular functions get 500 ms). Use it to drain.\n- **Logs are not per-request.** `stdout`/`stderr` are broadcast to all inflight requests of the instance, so correlate with your own request IDs.\n- **Same Function limits and Active CPU pricing** apply for size, memory, and duration.\n- **Secure Compute and Static IPs are not supported** with custom container images. If you need either, deploy that part without a container.\n- **Local dev**: `vercel dev` runs the image and requires the `docker` CLI plus a running daemon.\n\n### Multiple services in one project\n\nUse [Services](https://vercel.com/docs/services) to deploy several frontends/backends in one project, containerized or not. Set `runtime: \"container\"` on any service you want built as an image; `entrypoint` points at the Dockerfile relative to that service's `root`.\n\n```json filename=\"vercel.json\"\n{\n  \"services\": {\n    \"frontend\": { \"runtime\": \"container\", \"root\": \"frontend/\", \"entrypoint\": \"Dockerfile.vercel\" },\n    \"backend\":  { \"runtime\": \"container\", \"root\": \"backend/\",  \"entrypoint\": \"Dockerfile.vercel\" }\n  },\n  \"rewrites\": [\n    { \"source\": \"/api/(.*)\", \"destination\": { \"service\": \"backend\" } },\n    { \"source\": \"/(.*)\",     \"destination\": { \"service\": \"frontend\" } }\n  ]\n}\n```\n\nServices are internal by default — without a top-level rewrite, nothing is publicly routable. When `services` is present, build/runtime keys (`functions`, `buildCommand`, `installCommand`, `outputDirectory`, `framework`) move into the service and are no longer valid at the top level.\n\n### Vercel Container Registry (VCR)\n\n```bash\nvercel vcr login docker              # authenticate Docker with a short-lived OIDC token\nvercel vcr image ls my-app           # list images\nvercel vcr image inspect my-app <id>\nvercel vcr image rm my-app <id>\n```\n\nRegistry limits: 2 GB per compressed layer, 15 GB total image size, 4 MB manifest, 1 MB config blob. Layers must be gzip or zstd compressed — uncompressed OCI layers are rejected. Repositories per project: 10 (Hobby) / 1,000 (Pro) / 5,000 (Enterprise). Storage is billed at $0.10 per GB.\n\n### When to reach for a container\n\nGood fits: Go, Rust, Ruby, PHP, or other backends; apps needing system libraries like FFmpeg or Chromium; frameworks outside Vercel's auto-detection; guaranteed build/runtime parity across environments.\n\nPoor fits: anything that must hold state in-process, keep a daemon alive between requests, or run background work independent of a request. Reach for Workflow, Queues, or Cron for those.\n\nIf your framework is already auto-detected and you have no system-library needs, the standard build is simpler and faster — a Dockerfile is not an upgrade by default.\n\n## Plan Limits at a Glance\n\n| | Hobby | Pro | Enterprise |\n|---|---|---|---|\n| Duration (default / max) | 300s / **300s** | 300s / 800s | 300s / 800s |\n| Extended duration (beta) | — | 1800s | 1800s |\n| Memory / CPU | 2 GB / 1 vCPU, not configurable | Standard or Performance (4 GB / 2 vCPU) | Standard or Performance |\n| Bundle size | 250 MB (500 MB Python), 5 GB with large functions beta | same | same |\n| Concurrency | auto-scales to 30,000 | 30,000 | 100,000+ |\n| Regions | single region | up to 3 | all |\n| Edge code size (gzipped) | 1 MB | 2 MB | 4 MB |\n| VCR repos per project | 10 | 1,000 | 5,000 |\n| Request/response body | 4.5 MB | 4.5 MB | 4.5 MB |\n\n### What changed for Hobby\n\nHobby function limits went **up substantially** with Fluid Compute, and stale 10s/60s numbers are a common source of bad advice:\n\n- **Duration: 60s → 300s for both the default and the maximum** — a 5× increase. Hobby functions can run a full five minutes.\n- **CPU: the Basic instance was removed; Hobby now runs Standard**, 1 vCPU / 2 GB (up from 1 vCPU / 1.7 GB), managed by Vercel with a minimum of 1 vCPU.\n- Hobby still cannot configure memory/CPU, use the extended 30-minute duration, or run in multiple regions — those remain Pro/Enterprise.\n\n## Streaming\n\nZero-config streaming on the default Node.js runtime, including Server-Sent Events (SSE). Essential for AI applications.\n\n> **You do NOT need `runtime = 'edge'` for streaming or SSE.** Streaming responses (`ReadableStream`, `text/event-stream`) work on the default Node.js runtime — this is the single most common reason people wrongly reach for Edge. Stay on Node.js (Fluid Compute) so you keep full Node.js APIs, npm packages, and longer durations; Edge offers no streaming advantage and caps you at 25s to first byte.\n\n```ts\nexport async function POST(req: Request) {\n  const encoder = new TextEncoder()\n  const stream = new ReadableStream({\n    async start(controller) {\n      for (const chunk of data) {\n        controller.enqueue(encoder.encode(chunk))\n        await new Promise(r => setTimeout(r, 100))\n      }\n      controller.close()\n    },\n  })\n\n  return new Response(stream, {\n    headers: { 'Content-Type': 'text/event-stream' },\n  })\n}\n```\n\nFor AI streaming, use the AI SDK's `toUIMessageStreamResponse()` (for chat UIs with `useChat`) which handles SSE formatting automatically.\n\n## WebSockets\n\nVercel Functions can hold open bidirectional WebSocket connections — use them for realtime features like interactive AI streaming, chat, and collaborative apps. There is **no separate WebSocket-server product and no third-party service (Pusher, Ably, etc.) required** — it runs on Vercel Functions directly. Requires **Fluid Compute**, which is the default for new projects.\n\n**How it works**: a WebSocket starts as an HTTP `GET` with an `Upgrade` header, so it passes through the same Routing Middleware, rewrites, Firewall rules, and rate limits as any other request. After the upgrade, the connection is pinned to a single function instance for its lifetime; Fluid Compute lets one instance serve many concurrent connections. Active CPU pricing means you're billed while processing messages, not for idle open connections — the same limits and pricing as other Function invocations apply.\n\n### `ws` (no extra config)\n\nWebSockets work like any distributed WebSocket server — export an `http.Server` and use a library such as `ws`:\n\n```ts\n// api/ws.ts\nimport http from 'http'\nimport { WebSocketServer } from 'ws'\n\nconst server = http.createServer()\nconst wss = new WebSocketServer({ server })\n\nwss.on('connection', (ws) => {\n  ws.on('message', (data) => ws.send(data)) // echo\n})\n\nexport default server\n```\n\n### Socket.IO\n\nHigher-level realtime libraries like Socket.IO work too. Configure the **client** to use the WebSocket transport directly — Socket.IO defaults to HTTP long-polling, which won't work:\n\n```ts\n// api/socket-io.ts\nimport http from 'http'\nimport { Server } from 'socket.io'\n\nconst server = http.createServer()\nconst io = new Server(server)\n\nio.on('connection', (socket) => {\n  socket.on('message', (data) => socket.send(data))\n})\n\nexport default server\n```\n\n```ts\n// client.ts\nimport { io } from 'socket.io-client'\n\nconst socket = io('https://your-domain.com', {\n  // Socket.IO appends /socket.io, so the full path becomes /api/socket-io/socket.io\n  path: '/api/socket-io/socket.io',\n  transports: ['websocket'], // required — Socket.IO defaults to HTTP long-polling\n})\n```\n\nExpress, Hono, and Nitro (including Nuxt, via native WebSocket support) serve WebSockets the same way — export the HTTP server. Python frameworks work too: FastAPI handles the upgrade natively, and `python-socketio` is protocol-compatible with the JS Socket.IO client.\n\n### Next.js\n\nNext.js doesn't expose an API for handling WebSocket upgrades. Use `experimental_upgradeWebSocket()` from `@vercel/functions` inside a route handler:\n\n```ts\n// app/api/ws/route.ts\nimport { experimental_upgradeWebSocket, type WebSocketData } from '@vercel/functions'\n\nexport async function GET() {\n  return experimental_upgradeWebSocket((ws) => {\n    ws.on('message', (data: WebSocketData) => ws.send(data))\n  })\n}\n```\n\n### Reconnects and persistent state\n\n- **Connections close when the function reaches its max duration.** Clients must reconnect with backoff, then resubscribe to channels and reload any state they need.\n- **No instance affinity across connections.** A reconnect — or a new deployment — may land on a different instance, so never keep durable state, presence, rooms, or pub/sub coordination in memory. Use an external store such as [Redis from the Marketplace](https://vercel.com/marketplace/redis).\n\n```ts\n// client.ts — reconnect with exponential backoff\nlet socket: WebSocket\nlet delay = 1000\n\nfunction connect() {\n  socket = new WebSocket('wss://your-domain.com/api/ws')\n  socket.addEventListener('open', () => { delay = 1000 })\n  socket.addEventListener('message', (e) => console.log(e.data))\n  socket.addEventListener('close', () => {\n    setTimeout(connect, delay)\n    delay = Math.min(delay * 2, 30000)\n  })\n}\n\nconnect()\n```\n\n## Cron Jobs\n\nSchedule function invocations via `vercel.json`:\n\n```json filename=\"vercel.json\"\n{\n  \"crons\": [\n    {\n      \"path\": \"/api/daily-report\",\n      \"schedule\": \"0 8 * * *\"\n    },\n    {\n      \"path\": \"/api/cleanup\",\n      \"schedule\": \"0 */6 * * *\"\n    }\n  ]\n}\n```\n\nThe cron endpoint receives a normal HTTP request. Verify it's from Vercel:\n\n```ts\nexport async function GET(req: Request) {\n  const authHeader = req.headers.get('authorization')\n  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {\n    return new Response('Unauthorized', { status: 401 })\n  }\n  // Do scheduled work\n  return Response.json({ ok: true })\n}\n```\n\n## Configuration\n\n`vercel.ts` is the recommended way to configure a project — full TypeScript types, dynamic logic, and env access via `@vercel/config`. `vercel.json` remains fully supported. Legacy `now.json` support ended **March 31, 2026**; rename it to `vercel.json` (no content changes required).\n\n```ts\n// vercel.ts\nimport type { VercelConfig } from '@vercel/config/v1'\n\nexport const config: VercelConfig = {\n  functions: {\n    'app/api/heavy/**': { maxDuration: 800 },\n    'app/api/report/**': { maxDuration: 1800 }, // Pro/Ent extended-duration beta\n  },\n  crons: [{ path: '/api/cleanup', schedule: '0 0 * * *' }],\n}\n```\n\nThe `vercel.json` equivalent:\n\n```json\n{\n  \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n  \"functions\": {\n    \"app/api/heavy/**\": { \"maxDuration\": 800 },\n    \"api/upload.js\": { \"supportsCancellation\": true }\n  }\n}\n```\n\nWhat you **cannot** put here:\n- `memory` — with Fluid Compute (the default), set it in the dashboard; Pro/Enterprise only, and `vercel.json` warns at build time\n- A project-wide default above 800s — extended durations are per-function only\n\n`runtime: \"edge\"` is accepted here, but prefer leaving it out — see [Prefer Node.js over the Edge runtime](#prefer-nodejs-over-the-edge-runtime).\n\n## Common Pitfalls\n\n1. **`waitUntil` given a callback**: it takes a Promise. `waitUntil(fn())`, never `waitUntil(fn)` or `waitUntil(async () => {})` — the latter silently does nothing\n2. **Cold starts with DB connections**: use connection pooling (e.g. Neon's `@neondatabase/serverless`)\n3. **Reaching for the Edge runtime**: prefer Node.js — see [Prefer Node.js over the Edge runtime](#prefer-nodejs-over-the-edge-runtime)\n4. **Timeout exceeded**: raise `maxDuration` (800s Pro/Ent, 1800s in beta), or move to Workflow for anything longer\n5. **Bundle size**: standard limit is 250 MB uncompressed (500 MB Python). 5 GB needs the large functions beta, which existing projects must opt into with `VERCEL_SUPPORT_LARGE_FUNCTIONS=1`\n6. **Payload size**: request and response bodies cap at **4.5 MB** (`413 FUNCTION_PAYLOAD_TOO_LARGE`) — use Blob client uploads or streaming, not a bigger function\n7. **In-memory state**: Fluid shares instances across invocations and scales to zero — never keep sessions, rooms, or caches in process memory\n8. **Setting `memory` in `vercel.json`**: with Fluid Compute enabled this is not the place for it and the build warns — set it in the dashboard\n9. **Environment variables**: available in all functions automatically; use `vercel env pull` for local dev\n\n## Function Runtime Diagnostics\n\n### Timeout Diagnostics\n\n```\n504 FUNCTION_INVOCATION_TIMEOUT?\n├─ All plans default to 300s with Fluid Compute\n├─ How long does the work actually need?\n│  ├─ ≤ 300s → Already allowed on every plan; the timeout is a bug, not a limit\n│  ├─ 300–800s → Pro/Enterprise: set `maxDuration` in code or vercel.json\n│  ├─ 800–1800s → Pro/Enterprise extended-duration beta (30 min)\n│  │   ├─ Must be set PER FUNCTION — project defaults above 800s are ignored\n│  │   ├─ Runtimes: nodejs20/22/24.x, Bun 1.x/1.4.x, python3.12/3.13/3.14\n│  │   └─ Blocked if the project uses Secure Compute or Static IPs\n│  └─ > 30 min, or must survive crashes/deploys → Vercel Workflow\n├─ On Hobby? → 300s is both default AND max; no extension exists, upgrade to Pro\n├─ Client disconnected before the function finished?\n│  └─ HTTP/1.1 drops idle connections → stream heartbeat/progress data\n└─ DB query slow? → Add connection pooling, check cold start, use Global Config\n```\n\n### 500 Error Diagnostics\n\n```\n500 Internal Server Error?\n├─ Check Vercel Runtime Logs (Dashboard → Deployments → Functions tab)\n├─ Missing env vars? → Compare `.env.local` against Vercel dashboard settings\n├─ Import error? → Verify package is in `dependencies`, not `devDependencies`\n└─ Uncaught exception? → Wrap handler in try/catch, use `after()` for error reporting\n```\n\n### Invocation Failure Diagnostics\n\n```\n\"FUNCTION_INVOCATION_FAILED\"?\n├─ Memory exceeded (OOM)?\n│  ├─ Pro/Enterprise → switch to Performance (4 GB / 2 vCPU) in Settings → Functions\n│  │   └─ With Fluid compute, set it there, not in vercel.json (which warns at build)\n│  └─ Hobby → fixed at 2 GB / 1 vCPU; reduce per-request memory or upgrade\n├─ Crashed during init? → Check top-level await or heavy imports at module scope\n├─ Build failed with \"exceeded the unzipped maximum size of 250 MB\"?\n│  ├─ Trim with excludeFiles / outputFileTracingExcludes first\n│  └─ Then large functions beta: VERCEL_SUPPORT_LARGE_FUNCTIONS=1 (5 GB, Node/Bun/Python)\n├─ 413 FUNCTION_PAYLOAD_TOO_LARGE? → 4.5 MB body cap; use Blob client uploads or streaming\n└─ Container image? → Is it listening on port 80 (or $PORT)? Is it holding state between requests?\n```\n\n### Cold Start Diagnostics\n\n```\nCold start latency > 1s?\n├─ Moving to the Edge runtime is not the fix — Vercel recommends migrating off it\n├─ Fluid Compute enabled? → Reuses warm instances across concurrent invocations\n├─ Measuring in preview? → Bytecode caching is production-only; re-measure in prod\n├─ Large function bundle? → Audit imports, use dynamic imports, tree-shake\n├─ DB connection in cold start? → Use connection pooling (Neon serverless driver)\n└─ Container image? → Scales to zero after 5 min idle (30 s in preview); expect cold starts\n```\n\n### Edge Function Timeout Diagnostics\n\n```\n\"EDGE_FUNCTION_INVOCATION_TIMEOUT\"?\n├─ Edge must START the response within 25s (then may stream up to 300s)\n├─ `maxDuration` does NOT apply to the Edge runtime — there is no way to raise this\n├─ Recommended fix: drop `runtime = 'edge'` and run on Node.js\n│  └─ Node.js gives you 300s by default, 800s on Pro/Ent, 1800s in the beta\n└─ On Next.js 16.3+, `runtime = 'edge'` is unsupported — migration is required there\n```\n\n## Official Documentation\n\n- [Vercel Functions](https://vercel.com/docs/functions)\n- [Functions limits](https://vercel.com/docs/functions/limitations) — duration, memory, bundle size, large functions\n- [Configuring max duration](https://vercel.com/docs/functions/configuring-functions/duration) — including the extended 30-minute beta\n- [Configuring memory / CPU](https://vercel.com/docs/functions/configuring-functions/memory)\n- [Functions API reference](https://vercel.com/docs/functions/functions-api-reference) — `waitUntil`, `getDeadline`, SIGTERM, cancellation\n- [Fluid Compute](https://vercel.com/docs/fluid-compute)\n- [Container Images](https://vercel.com/docs/functions/container-images) — Dockerfile on Vercel\n- [Vercel Container Registry](https://vercel.com/docs/container-registry) and its [limits and pricing](https://vercel.com/docs/container-registry/limits-and-pricing)\n- [Services](https://vercel.com/docs/services) — multiple backends/frontends in one project\n- [Streaming](https://vercel.com/docs/functions/streaming-functions)\n- [WebSockets](https://vercel.com/docs/functions/websockets)\n- [Cron Jobs](https://vercel.com/docs/cron-jobs)\n- [Vercel Workflow](https://vercel.com/docs/workflows) — for anything beyond 30 minutes\n- [Edge Runtime](https://vercel.com/docs/functions/runtimes/edge) — legacy; Vercel recommends migrating to Node.js\n- [GitHub: Vercel](https://github.com/vercel/vercel)\n"}],"summary":"Fields changed: 2. /description, /skill_md_contents.","summary_kind":"deterministic","summary_metadata":{}}