Managed vs self-hosted WebSocket services #

Your product needs real-time features and the team is split. One side wants a managed service — Pusher, Ably, PubNub, Azure Web PubSub, or a cloud provider’s WebSocket gateway — so nobody has to learn about file-descriptor limits, sticky sessions and reconnect storms. The other side points at the per-message price, the vendor’s SDK in every client, and the day the bill overtakes an engineer’s salary. Both are right about something. A managed service converts the operational work described across this site into a monthly invoice and an integration; self-hosting converts the invoice into engineering time and on-call load. Which is cheaper and safer depends on scale, message patterns, team skills and constraints that are easy to write down in advance.

Root cause #

Running WebSockets well at scale is a real body of work: connection limits and OS tuning, load balancing with draining and rebalancing, cross-node fan-out, presence, delivery guarantees, multi-region failover, observability, and security at the handshake. Managed services have already built all of it, operate it for many customers, and expose it through a pub/sub API: publish to a channel from your backend, subscribe from clients with the vendor’s SDK.

What you give up is control and a linear relationship between usage and cost. Pricing is typically per connection, per message delivered (each fan-out recipient counts), or both, so a broadcast to a 5,000-member channel costs 5,000 messages. Your clients speak the vendor’s protocol, so switching later means changing every client. Data flows through a third party, which matters for regulated industries and data residency. And features you need that the vendor lacks — a particular ordering guarantee, a custom auth flow, server-side filtering — are either impossible or require workarounds.

Illustrative monthly cost by scale At small scale a managed service is several times cheaper than self-hosting once engineering time is included; around a hundred thousand connections they are similar; at a million connections with heavy fan-out, usage-based managed pricing costs about three times as much as self-hosting. Illustrative monthly cost by scale thousands of dollars; self-hosted includes a share of engineering time managed (usage-based) self-hosted (infra + eng time) 0k$ 25k$ 50k$ 75k$ 100k$ 10k peak conns, low traffic 100k conns, moderate 90k$ 30k$ 1M conns, heavy fan-out
The curves cross — where depends on your message fan-out, not just your connection count.

Resolution #

Decide with a written comparison against your own numbers rather than general opinion. Estimate three quantities from product analytics or a pilot: peak concurrent connections, messages published per month, and average fan-out per message (recipients per publish). Delivered messages — the unit most vendors bill — are published × fan-out. Then compare each option’s monthly cost and the engineering time each implies. A small calculator makes the assumptions explicit and reviewable:

interface Workload {
peakConnections: number;
publishesPerMonth: number;
avgFanout: number; // recipients per published message
}

interface ManagedPricing {
perMillionMessages: number; // $ per million delivered messages
perPeakConnection: number; // $ per peak concurrent connection per month
includedMessages?: number;
}

interface SelfHostedModel {
connectionsPerNode: number; // measured in load tests
nodeMonthly: number; // $ per node incl. LB/pub-sub share
engineerMonthly: number; // fully loaded $ per engineer-month
engineerFraction: number; // share of an engineer to operate it (on-call, upgrades)
minNodes: number; // redundancy floor (e.g. 3 across zones)
}

export function compare(w: Workload, m: ManagedPricing, s: SelfHostedModel) {
const delivered = w.publishesPerMonth * w.avgFanout;
const billable = Math.max(0, delivered - (m.includedMessages ?? 0));
const managed = (billable / 1e6) * m.perMillionMessages + w.peakConnections * m.perPeakConnection;

const nodes = Math.max(s.minNodes, Math.ceil(w.peakConnections / s.connectionsPerNode) * 1.3); // 30% headroom
const selfHosted = nodes * s.nodeMonthly + s.engineerMonthly * s.engineerFraction;

return { deliveredMessages: delivered, managed: Math.round(managed), selfHosted: Math.round(selfHosted), nodes: Math.ceil(nodes) };
}

// Example: a collaboration product with modest rooms.
console.table(compare(
{ peakConnections: 80_000, publishesPerMonth: 400e6, avgFanout: 6 },
{ perMillionMessages: 2.5, perPeakConnection: 0.015 },
{ connectionsPerNode: 40_000, nodeMonthly: 220, engineerMonthly: 18_000, engineerFraction: 0.25, minNodes: 3 },
));

The numbers above are placeholders — use your vendor’s actual price list and your measured per-node capacity from capacity planning for WebSocket fleets. The shape of the answer is what matters: fan-out multiplies managed costs but barely changes self-hosted costs, while the engineering fraction dominates self-hosting at small scale.

Cost is only one axis. Weigh the others explicitly:

Beyond cost: what each option commits you to Managed services win on time to production and operations, while self-hosting wins on protocol control, compliance, custom semantics and cost at large fan-out. Beyond cost: what each option commits you to Managed service Self-hosted Time to production days weeks to months On-call and upgrades vendor's yours Protocol / client lock-in vendor SDK your protocol Data residency, compliance vendor-dependent full control Custom semantics within the API anything Cost at large fan-out grows per delivery grows per node Many teams start managed and migrate the heaviest channels to self-hosted later
Managed buys speed and sleep; self-hosted buys control and cost at scale.

Keeping the exit open #

Whichever you choose, protect the ability to change your mind. Wrap the real-time client in a thin interface of your own — subscribe(channel, handler), publish(channel, data), onStatus(cb) — and keep vendor SDK calls behind it, so switching is a change in one module plus a server-side publisher, not a rewrite of every feature. Keep your message envelope and sequence numbers in your own payloads rather than relying on vendor-specific metadata, following designing a WebSocket message envelope. And publish from your backend through a single outbound adapter, which becomes the natural place to dual-publish during a migration.

A common trajectory is hybrid: stay on a managed service for most features, and move one high-volume, high-fan-out stream (market data, game state, telemetry) to a self-hosted fleet once its bill justifies it. The abstraction layer makes that a per-channel decision.

Edge cases #

Hidden fan-out. Presence and typing indicators generate delivered messages quadratic in room size. On usage-based pricing, these ephemeral features can dominate the bill; throttle them as in typing indicators over WebSockets regardless of hosting model.

Connection limits per plan. Managed plans cap concurrent connections; exceeding them rejects new clients. Monitor your peak against the cap and understand the vendor’s behaviour at the limit.

Outage dependency. A managed service’s incident is your incident, and you cannot fix it. Check the vendor’s status history and SLA, and decide whether critical features need a fallback path (for example, polling your own API) during vendor outages.

Verification #

Validate the decision with a pilot rather than a spreadsheet alone. Run one real feature on the chosen option for a few weeks and record connections, delivered messages, latency percentiles, incident count and engineering hours spent. Compare the actual bill (or infrastructure cost plus time) against the calculator’s prediction; if they differ by more than a factor of two, the model’s fan-out or capacity assumptions were wrong and the decision deserves another look.

A typical real-time hosting trajectory A product launches on a managed service, adds a client abstraction layer at nine months, finds at eighteen months that one stream dominates the bill, moves that stream to a self-hosted fleet at twenty-two months and settles into a hybrid reviewed yearly. A typical real-time hosting trajectory launch on managed service (0 mo) abstraction layer added (9 mo) market-data stream costs 60% of bill (18 mo) that stream self-hosted (22 mo) hybrid, reviewed yearly (36 mo) The abstraction layer is what made month 22 a small project
Decide per stream, and keep the switch cheap.

Operational checklist #

FAQ #

Is it cheaper to use Pusher or Ably than to run my own WebSocket servers? #

At small and moderate scale, usually yes once engineering time is counted. At large scale — especially with high fan-out — usage-based pricing often exceeds the cost of running your own fleet. Compute both with your own connection and delivery numbers.

What is the biggest risk of a managed WebSocket service? #

Lock-in: clients speak the vendor’s protocol and SDK, so leaving requires changing every client. An internal abstraction layer and your own message envelope keep that risk small.

Can I mix managed and self-hosted? #

Yes, and many teams do: most channels on a managed service, the highest-volume streams on self-hosted infrastructure, behind one client interface.

Where do API Gateway and Durable Objects fit? #

They are serverless infrastructure rather than full managed real-time products: you still write the fan-out, presence and delivery logic, but you do not run servers. See AWS API Gateway WebSocket APIs and Cloudflare Durable Objects WebSocket hibernation.

Back to Serverless & Managed WebSockets.