Guide for building Vercel runtimes that implement the Fluid IPC protocol.
Reference implementations:
- Node.js:
packages/node/(interpreted, primary reference) - Python:
packages/python/(interpreted, WSGI/ASGI) - Rust:
packages/rust/+crates/vercel_runtime/(compiled)
Documentation: DEVELOPING_A_RUNTIME.md
Fluid compute enables HTTP streaming, request multiplexing, and efficient resource utilization via a secure TCP-based IPC protocol between Vercel's infrastructure and function instances.
The Rust core wraps language processes, communicating via HTTP locally and IPC to the Function Router for response streaming and health metrics.
Connect to Unix socket at VERCEL_IPC_PATH. Messages are JSON terminated with \0.
server-started (required, once at startup):
{
"type": "server-started",
"payload": { "initDuration": 150, "httpPort": 3000 }
}handler-started (required, per request):
{
"type": "handler-started",
"payload": {
"handlerStartedAt": 1704067200000,
"context": { "invocationId": "abc123", "requestId": 42 }
}
}end (required, per request):
{
"type": "end",
"payload": {
"context": { "invocationId": "abc123", "requestId": 42 },
"error": null
}
}log (optional, base64-encoded message):
{
"type": "log",
"payload": {
"context": { "invocationId": "abc123", "requestId": 42 },
"message": "SGVsbG8=",
"level": "info"
}
}Use "stream": "stdout" or "stderr" instead of level for raw output.
metric (optional, for fetch instrumentation):
{"type": "metric", "payload": {"context": {...}, "type": "fetch-metric", "payload": {"pathname": "/api", "duration": 45, "host": "example.com", "statusCode": 200, "method": "GET", "id": 1}}}Extract and remove before passing to user code:
x-vercel-internal-invocation-id→invocationId(string)x-vercel-internal-request-id→requestId(integer)x-vercel-internal-span-id,x-vercel-internal-trace-id→ remove
Return HTTP 200 for /_vercel/ping. Do NOT send IPC messages.
- Connect to
VERCEL_IPC_PATHUnix socket - Send
server-startedafter HTTP server binds - Extract/remove
x-vercel-internal-*headers per request - Send
handler-startedat request start - Set up request context storage (thread-local/context vars)
- Intercept stdout/stderr/logging → IPC with context
- Send
endafter each request (even on errors) - Handle
/_vercel/pinghealth checks - Buffer logs before
server-started, flush after or to stderr on exit - Support concurrent requests
- Implement
waitUntilAPI (wait for promises before exit with 30s timeout)
- Export
version = 3 - Export
build()→{ output: Lambda } - Export
startDevServer()forvercel dev(optional) - Export
prepareCache()for build caching (optional)
See DEVELOPING_A_RUNTIME.md for full API documentation.
Reference: packages/node/src/build.ts, packages/python/src/index.ts
Use standard Lambda runtimes (nodejs22.x, python3.12, etc.).
Reference: packages/rust/src/index.ts
Use runtime: 'executable' with runtimeLanguage: 'rust' | 'go' for IPC orchestration. The handler must be named executable.
| Property | Description |
|---|---|
handler |
Entry point (index.handler for node, executable for compiled) |
runtime |
nodejs22.x, python3.12, or executable for compiled |
runtimeLanguage |
'go' or 'rust' for executable runtime |
architecture |
'x86_64' or 'arm64' |
supportsResponseStreaming |
Enable streaming responses |
- Missing
\0terminator on IPC messages - Forgetting base64 encoding for log messages
- Not removing
x-vercel-internal-*headers - Blocking on IPC during init (buffer logs first)
- Missing
/_vercel/pinghandler - Not sending
endon errors - Blocking concurrent requests (limits Fluid compute benefits)
- Buffering entire responses (stream chunks instead)
- Use
peerDependenciesfor@vercel/build-utils - Reference via
"use": "@vercel/your-runtime"in vercel.json - Create changeset:
pnpm changeset