supabase/realtime documents its Postgres limits more carefully than its delivery
Broadcast, Presence, and Postgres Changes via WebSockets. This is a server built with Elixir using the Phoenix Framework that enables the following functionality: Broadcast: Send ephemeral messages from client to clients with low latency.
At a glance
- What is it?
- An Elixir and Phoenix server that provides Broadcast, Presence and Postgres Changes over WebSockets, with no message delivery guarantee and a compatibility table that names exact Postgres patch versions, the OrioleDB bugs it inherits, and which operations need a superuser. Two of its three features did not exist in v1.
- Who is it for?
- supabase/realtime fits a Supabase application that needs ephemeral messages, shared presence state and database change streams over one connection, and whose team is willing to read a compatibility matrix before choosing a tenant database version. It does not fit a system that needs at-least-once delivery, because the server says it does not guarantee that every message arrives.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Elixir, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- supabase/realtime7,683 ★ · +75
| Repository | 2026-07-31 | 2026-11-13 |
|---|---|---|
| supabase/realtime | 7.6k | 7.7k |
75 Stars in the last 105 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
No delivery guarantee, in its own heading
One section of the readme is a question with a short answer, and it is the first thing to internalise before designing anything on top. The section asks whether the server guarantees message delivery, and answers that it does not guarantee that every message will be delivered to your clients, with an instruction to keep that in mind as you use Realtime.
That is not a caveat at the end of a feature list. It is the boundary condition of the whole service.
The three features are otherwise conventional. Broadcast sends ephemeral messages from client to clients with low latency. Presence tracks and synchronises shared state between clients. Postgres Changes listens to Postgres database changes and sends them to authorised clients. The distinction in that last one is the word authorised, which is the point where the server's own access control enters the picture rather than the client's.
Two of the three features did not exist in v1
The status table is a compact version history. Postgres Changes is marked present in both v1 and v2, and both are labelled GA. Broadcast and Presence have empty marks in the v1 column and are labelled GA in v2.
So a v1 deployment gave you database change streams and nothing else, and two thirds of the current feature set arrived with the major version bump.
The readme says the repository focuses on version 2, while the previous version's code and Docker image remain accessible, and points at Docker Hub for the latest images. That is the pattern of a service where the v1 line is frozen rather than deprecated, which is what you want to know if you have a v1 deployment you are considering leaving.
The rest of the framing is candid: the codebase is under heavy development and the documentation is constantly evolving, and the project asks you to try it and file an issue.
The Postgres compatibility table is the real documentation
Most projects state their database requirements in a sentence. This one publishes a table with a tenant database column, a role column, a status column and a notes column, and the notes are specific enough to be useful.
Anything below Postgres 14 is not supported. Postgres 14 is supported with limitations. From 15.14.1.018 onwards, and across 16 and 17, the status is simply Supported. OrioleDB builds below 17.11.0.004 are supported with limitations and later ones are simply Supported, and Multigres is listed as supported with the note that it must be at least 17.x and that every commit waits for a standby.
That last note is the kind of detail you cannot infer. A reader learns not only that Multigres works, but that it is a different consistency model from the Postgres it reports as 17.x, which changes what to expect from a change stream.
The versions in the table refer to supabase/postgres releases unless stated otherwise, so the numbers are Supabase's builds rather than upstream PostgreSQL versions.
The superuser requirement moves at 15.14.1.018
Follow the role column across the rows and a pattern appears. Every supported configuration names supabase_admin as the role, and the notes track which operations actually need a superuser.
On 14.x, Realtime needs a superuser, and specifically only a superuser can set log_min_messages, because supautils cannot hand that setting to another role on that version. There is a second bug on the same row: on 14.5 and earlier, a call to realtime.broadcast_changes fails when a trigger invokes it with PERFORM.
On 15.x below 15.14.1.018 the server still needs a superuser, and there is a narrower gap: supautils policy grants do not cover the realtime.subscription policy until a specific supabase/postgres commit.
From 15.14.1.018 onwards, and across 16 and 17, the requirement narrows to migrations needing a superuser while tenant grants do not, and supautils manages the policies. So the practical advice is simple: do not deploy on a version earlier than 15.14.1.018 unless you have a specific reason, and the reason will be a superuser requirement you could otherwise avoid.
OrioleDB inherits two documented bugs
The OrioleDB rows are the most detailed in the table and worth reading in full, because they describe real message loss rather than a configuration gap.
On builds below 17.11.0.004, with REPLICA IDENTITY FULL set, Postgres Changes UPDATE and DELETE events can miss the old value of large TOASTed columns.
The same row records a second problem: Broadcast from Database can lose messages sent from a trigger. From 17.11.0.004 onwards the status is simply Supported, with only the note that migrations need a superuser, which is worth keeping in mind before choosing an older OrioleDB build.
Four compose files describe one local environment
The development environment is assembled from compose files rather than a single one. The root compose file includes compose.dbs.yml and defines a single service, realtime, which depends on two databases, realtime_db and tenant_db, and builds from the local Dockerfile.
The environment block is where the shape of the service becomes visible. There is a search_path set to the _realtime schema after connect, encryption keys for the database, a distinct JWT secret for metrics alongside the API one, a node name, a region, and a file descriptor limit raised to a million. Distribution flags are set through ERL_AFLAGS and an Erlang VM heap limit (+hmax) through ELIXIR_ERL_OPTIONS, and two separate RPC ports are declared for the client and server sides of the cluster. Self-host seeding is switched on for development, and the port is published to the host with a host gateway alias for container-to-host access.
The separate files for tenant databases, realtime databases, tests and Walrus databases each isolate one dependency, which is what lets a single file stay short.
The image ships a lazily extracted binary wrapper
The Dockerfile does more than compile Elixir. It builds a second binary in a stage named for pg-delta, a Supabase tool pinned to an alpha version and to a specific commit, and then does something unusual with it.
A Bun toolchain at a pinned version installs the project, a TypeScript entry point is compiled to a native binary, and that binary is compressed with xz at maximum threads. Alongside it, a small shell wrapper is generated: it checks whether the binary exists under the application cache directory, creates the directory if needed, decompresses the archive on first run, marks the result executable and then execs it with the arguments it was given.
So the shipped image contains a compressed payload that materialises itself the first time it is invoked, and the rest of the build removes the intermediate artefacts including the Bun installation. The builder stage then runs the Elixir build with MIX_ENV set to prod, using a pinned Elixir and Erlang version on a pinned Debian base.
The toolchain files say how the project is checked
Four root files describe the static analysis and release process, and they are worth reading as a checklist of what the project considers part of correctness.
.credo.exs configures Credo, the Elixir static analyser. .dialyzer_ignore.exs holds suppressions for Dialyzer, the type checker. .formatter.exs defines the formatting rules, and .sobelow-conf configures Sobelow, which finds security issues in Erlang and Elixir source.
Release automation is declarative: a .releaserc file configures semantic-release, and the releases themselves are currently in the v2.151 range, with the last three landing within a day.
The rest of the tree is documentation in a form you can grep. ARCHITECTURE.md covers how the cluster, broadcast fan-out and Postgres Changes fit together, ENVS.md is the full environment variable list matching the compose block, ERROR_CODES.md lists operational codes, OBSERVABILITY_METRICS.md covers monitoring, and DEVELOPERS.md points at mise tasks for local setup.
Editorial conclusion
supabase/realtime fits a Supabase application that needs ephemeral messages, shared presence state and database change streams over one connection, and whose team is willing to read a compatibility matrix before choosing a tenant database version. It does not fit a system that needs at-least-once delivery, because the server says it does not guarantee that every message arrives. Before deploying, pick a Postgres version that is fully supported rather than merely supported with limitations, and read ERROR_CODES.md and ENVS.md, which are where the operational behaviour is actually specified.
Frequently asked questions
What does supabase/realtime do?
It is an Elixir server built with the Phoenix Framework that provides three things over WebSockets: Broadcast for sending ephemeral messages from client to clients with low latency, Presence for tracking and synchronising shared state between clients, and Postgres Changes for listening to database changes and sending them to authorised clients.
Does supabase/realtime guarantee message delivery?
No. The readme has a section asking exactly that and answers that the server does not guarantee every message will be delivered to your clients, and asks you to keep that in mind as you use it.
Which Postgres versions does supabase/realtime support?
Anything below 14 is not supported. Postgres 14 is supported with limitations, 15.x from 15.14.1.018 onwards plus 16 and 17 are supported, and OrioleDB builds are supported with limitations below 17.11.0.004 and supported from 17.11.0.004 onwards. The version numbers refer to supabase/postgres releases, and the notes record which operations require a superuser.
How do I run supabase/realtime locally?
The readme points at DEVELOPERS.md for local setup, mise tasks and example workflows. The compose files in the repository start the service with a realtime database and a tenant database, and the quick start also links the Supabase UI Library Realtime components and the multiplayer.dev demo repository.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/supabase-realtime)Star history chart for your README
[](https://hysenlabs.com/projects/supabase-realtime)Was this useful?