Why Serverless Functions Can't Hold WebSocket Connections (And How to Fix It)
A technical deep-dive into why WebSockets are incompatible with serverless architecture, and the practical solutions available for adding real-time features to your apps.
"Why can't I just open a WebSocket in my API route?"
This is one of the most common questions from developers new to serverless. The answer reveals a fundamental architectural incompatibility — and understanding it will help you design better real-time systems.
The Promise of WebSockets
Traditional HTTP works like this: Every interaction requires a new request. If you want to know when something changes, you have to keep asking.
WebSockets flip this model: One connection, bidirectional communication, server can push whenever it wants. Perfect for chat, notifications, live dashboards, multiplayer games.
How Serverless Functions Work
Serverless functions (AWS Lambda, Vercel Functions, Cloudflare Workers) work fundamentally differently from traditional servers.
Traditional Server
The server is always running. It keeps connections open in memory. When you want to send a message to User #2, you find their connection object and call connection.send().
Serverless Function
Key characteristics:
- Ephemeral — Functions don't run continuously
- Stateless — No memory between invocations
- Request/Response — One input, one output, done
- Timeout limits — Can't run forever (10s-300s depending on platform)
The Incompatibility
WebSockets need:
- Persistent connections (hours or days)
- State (tracking who's connected)
- Ability to send messages at any time
- No timeout
Serverless provides:
- Short-lived execution (seconds to minutes)
- Stateless (no persistent memory)
- Request/response model only
- Hard timeout limits
It's not that serverless is bad — it's that the two models are architecturally incompatible.
Solution 1: Separate the Concerns
The modern solution is to separate WebSocket handling from your application logic.
- Clients connect to the WebSocket service (not your serverless functions)
- WebSocket service holds the connections
- Your serverless functions publish messages via REST API
- WebSocket service delivers to connected clients
Your functions stay stateless. WebSocket connections are someone else's problem — see how this works in practice with WebSockets for Vercel or any serverless platform.
Code Example
// Client: Connect to PushFlo, not to your server
import { PushFloClient } from '@pushflodev/sdk';
const client = new PushFloClient({ publishKey: 'YOUR_PUBLISH_KEY' });
client.connect();
client.subscribe('notifications', {
onMessage: (data) => {
showNotification(data);
},
});
// Server: Your serverless function publishes via REST
export async function POST(request: Request) {
// Do your business logic
const result = await processOrder(request);
// Notify user via WebSocket service
await fetch('https://api.pushflo.dev/api/v1/publish', {
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_KEY' },
body: JSON.stringify({
channel: 'notifications',
data: { message: 'Order complete!' }
})
});
return Response.json(result);
}
Solution 2: Edge Functions + Streaming (Limited)
Some platforms offer edge functions with streaming support, like Server-Sent Events. Good for streaming AI responses, not for real-time multiplayer.
Solution 3: Cloudflare Durable Objects (Platform-Specific)
If you're all-in on Cloudflare, Durable Objects can hold WebSocket connections. Powerful but vendor lock-in.
Comparison of Solutions
| Solution | Complexity | Cost | Vendor Lock-in | Best For |
|---|---|---|---|---|
| Managed Service (PushFlo) | Low | $ | Low | Most projects |
| Edge + SSE | Medium | $ | Medium | Streaming responses |
| Durable Objects | High | $$ | High (Cloudflare) | Cloudflare-native |
| Hybrid | High | $$$ | Low | Custom requirements |
The Mental Model
Think of it this way:
Serverless functions are like phone calls — you connect, talk, hang up.
WebSockets are like leaving the phone line open all day.
You can't leave a phone call open on a system designed for quick calls.
The solution isn't to fight the architecture. It's to use a service designed for keeping lines open, and have your serverless functions communicate with it.
Conclusion
Serverless functions can't hold WebSocket connections because:
- They're ephemeral (functions terminate)
- They're stateless (no connection tracking)
- They have timeouts (connections would die)
- The request/response model doesn't fit bidirectional communication
The fix is architectural separation:
- Let a WebSocket service hold connections
- Your serverless functions publish messages via REST
- Users get real-time updates
- You keep your serverless benefits
This isn't a workaround — it's the correct architecture for serverless + real-time.
Ready to add real-time to your serverless app? Get started with PushFlo — we hold the WebSocket connections so your functions don't have to.
Add real-time to your serverless stack
PushFlo manages persistent WebSocket connections so your functions stay stateless.
Related Articles
How to Add Real-time Features to Next.js Without Managing Servers
Learn how to add WebSocket-powered real-time features like live notifications, chat, and dashboards to your Next.js app deployed on Vercel — without running your own WebSocket server.
WebSocket Alternatives for Vercel and Cloudflare Workers
Vercel and Cloudflare Workers don't support WebSockets natively. Here are your options for adding real-time features to serverless apps, from polling to managed services.
Polling vs WebSockets: Cost Comparison for Serverless Apps
A detailed breakdown of the true costs of polling vs WebSocket-based real-time features in serverless applications. Includes calculations for Vercel, AWS Lambda, and managed WebSocket services.
