Realtime capacity and limits
Realtime limits on SupaHost. Per-project rate limits for events, joins, channels and bandwidth, measured connection capacity per plan, and how to stay within them. No per-message fees.
Realtime on SupaHost has no per-message, per-connection or per-channel fees. It's part of your instance's flat monthly price. Two things set how far it goes: fixed rate limits for each project, and the capacity of your instance, which all its projects share.
Per-project limits
Each project is its own tenant on the instance's Realtime server, with these limits. They are well above open-source Realtime's defaults (200 concurrent users and 100 events per second).
| Limit | Value per project | What counts |
|---|---|---|
| Concurrent users | 50,000 | Open client connections to the project |
| Events per second | 5,000 | Messages handled by Realtime: Broadcast sends, Presence updates, Postgres Changes deliveries |
| Channel joins per second | 500 | Clients joining channels, across the project |
| Channels per client | 100 | Channels one connection has joined at the same time |
| Bytes per second | 10,000,000 (about 10 MB/s) | Message data handled for the project |
For Postgres Changes, Realtime checks each project's database for changes every 100 ms, and a single change record can be up to 1 MB.
These limits are fixed and the same on every plan. In practice the instance runs out of CPU and memory before a project reaches 50,000 connections, so plan with the capacity guides below.
Instance capacity by plan
SupaHost measured these with load tests. They're guides for one whole instance, shared by all its projects, not guarantees:
| Plan | Server | Concurrent Realtime connections (guide) | Monthly active users (guide, whole app) |
|---|---|---|---|
| Builder | 2 shared vCPU, 4 GB RAM | about 5,000 | about 25,000 |
| Pro | 2 vCPU, 8 GB RAM | about 15,000 | about 50,000 |
| Business | 4 vCPU, 16 GB RAM | about 30,000 | about 100,000 |
In a separate test, a Business instance held 15,000 concurrent Realtime connections. The monthly active user figures assume a typical app where about 5% of monthly users are active in the busiest hour, each making a request every 10 seconds.
What a connection costs depends on what it does. An idle connection on one channel is cheap. Connections that send many messages, track Presence often, or subscribe to busy tables with Postgres Changes use more CPU. The database, API and Auth share the same server, so heavy Realtime use leaves less room for them, and the other way round.
If you need more, move to a larger plan. Changing plan resizes the VM, with about a minute or two of downtime. See Changing plans.
What happens at a limit
- Too many channels on one client: joining the 101st channel fails with a
TooManyChannelserror. This almost always means channels are created without being removed. See TooManyChannels error. - Rate limits (events, joins, bytes): Realtime starts rejecting or dropping work for that project until the rate falls. Clients may see send errors, failed joins, or channels moving to
CHANNEL_ERROR. Other projects on the instance keep their own limits. - Instance capacity: when the server is overloaded, everything slows down: messages arrive late, joins time out, and API requests get slower. Check for instance health alerts and see High CPU usage on your instance.
Staying within limits
Remove channels you no longer need. Call supabase.removeChannel(channel) when a screen closes, and avoid creating channels in render loops.
Share channels. One channel per room, document or board is usually right. One channel per user per feature adds up quickly.
Throttle chatty events. Cursor positions and typing indicators don't need 60 updates a second. 10 to 20 per second for cursors and one per few seconds for typing is plenty.
// Send at most one cursor update every 50 ms
let last = 0
window.addEventListener('pointermove', (e) => {
const now = performance.now()
if (now - last < 50) return
last = now
channel.send({ type: 'broadcast', event: 'cursor', payload: { x: e.clientX, y: e.clientY } })
})
Keep payloads small. Send ids and short values, not whole records. Large payloads use the bytes-per-second budget and your egress.
Prefer Broadcast from the database for busy tables. Postgres Changes checks RLS for every subscriber on every change. When many clients watch the same table, a trigger that calls realtime.send() to a topic scales better. See Postgres Changes.
Reconnect gently. After a network drop, supabase-js reconnects with a back-off. If you build your own reconnect logic, add jitter so thousands of clients don't rejoin in the same second, which would hit the 500 joins per second limit.
Egress
Realtime traffic goes through your instance's gateway, so data sent to clients counts toward your plan's monthly egress (250 GB on Builder, 500 GB on Pro, 1 TB on Business, shared across projects). A Broadcast message sent to 1,000 subscribers is sent 1,000 times. See Egress on SupaHost explained.
Comparing with Supabase Cloud
On Supabase Cloud's Pro plan, Realtime includes 500 concurrent connections and 5 million messages a month, then charges $10 per 1,000 connections and $2.50 per million messages. On SupaHost those are covered by the instance price; the limits are the ones on this page.