拆开 108K Star 的 supabase 仓库,里面一行数据库代码都没有
- supabase/supabase(108K Star)是个门面仓库,核心引擎散落在 PostgREST、auth、realtime 等独立仓库里,主仓库装的是仪表盘、文档站和营销官网
- 自托管版本由 docker-compose 的 13 个服务块组成,包含 Envoy 网关、GoTrue 认证、PostgREST、realtime、edge-runtime 等,一半是第三方开源件,一半是 Supabase 自研
- 权限链路不走应用层:JWT 由 auth 签发 -> PostgREST 校验并映射角色 -> Postgres RLS 在行级用 auth.uid() 过滤,策略写漏一行就是数据越权
- Kong 已从默认网关降级为可选配置换成 Envoy,但 docker/versions.md 还在给 Kong 记版本账,文档密度大到官方自己顾不过来,2026 年 Postgres 从 15 直接跳到 17
- 贡献者前 4 名全是员工,核心栈按公司节奏走;拼装式开源 + 自研比例上升(Multigres HA 即将公测),逃生通道是 pg_dump 或直连连接串随时搬走
上周我想搞清楚一件事,Supabase 的数据库到底是怎么封装的。于是把 supabase/supabase 这个仓库 clone 了下来,2.3GB,108,710 个 Star,等了挺久。
结果翻完目录我愣住了。
这个仓库里没有数据库引擎,没有 SQL 解析器,连一行存储层的代码都没有。主语言是 TypeScript,最大的几个目录是 apps、packages、docker,数据库本体在哪?不在。它住在完全独立的另一个仓库里,而且那个仓库 2019 年 Supabase 公司成立之前就存在了。
你想想看,一个以「开源 Firebase 替代品」闻名的项目,它最核心的 GitHub 仓库其实是个门面。今天就把这个门面拆开,看看 Supabase 的真实架构长什么样,顺便聊聊这种做法背后的工程哲学。
先说这个壳里装了什么
supabase/supabase 是个 Turborepo 管的 pnpm monorepo,DEVELOPERS.md 里写得很清楚。三个大头,apps 目录放着 studio(仪表盘)、docs(文档站)、www(营销官网)、learn、kb(知识库)、design-system,一共 8 个应用。packages 目录放着 ui 组件库、pg-meta、api-types、ai-commands 这些共享包。docker 目录是自托管发行版,一堆 compose 文件。
坦白讲,第一次意识到「营销官网也是这个仓库的一部分」时,我对 2.3GB 的磁盘占用就释怀了。apps/studio 一个应用的 package.json 里挂了 122 个依赖,这是整个仓库里最重的产品代码。
还有个容易被忽略的细节,仓库根目录下有个 supabase/ 文件夹,里面是 config.toml、migrations、seed.sql、buckets。这是他们自己用 Supabase CLI 建的本地项目结构,等于说 Supabase 仓库自己就是一个 Supabase 项目,开发文档站要连自家数据库。自己家的狗粮自己先吃,这个味道是对的。
那真正的引擎在哪?
真身在 compose 文件里
README 的架构图会告诉你,Supabase 是一组开源工具的组合。但其实吧,架构图是画的,docker/docker-compose.yml 是真的,587 行,13 个服务块,每个服务的镜像和版本都写得明明白白。这张表才是 Supabase 的真身。
从上往下过一遍。api-gw 是网关,跑的是 envoyproxy/envoy v1.39.0,所有请求先进这里再分流。auth 服务跑 supabase/gotrue v2.189.0,管注册登录,签发 JWT。rest 服务跑 postgrest/postgrest v14.12,把 Postgres 的表直接变成 REST API,这个是整个产品的灵魂,待会细说。realtime v2.102.3 负责把数据库变更广播到 WebSocket。storage v1.60.4 管文件,旁边挂了个第三方的 darthsim/imgproxy v3.30.1 做图片缩放。meta 服务跑 postgres-meta v0.96.6,给仪表盘提供表结构管理接口。functions 跑 edge-runtime v1.74.0,在服务端执行 Deno 函数。最底下是 db,supabase/postgres 17.6.1.136 镜像,旁边站着 supavisor 2.9.5 做连接池。
有意思的是这 13 个服务里,真正由 Supabase 自己写的大概一半。Envoy 是 CNCF 毕业项目,PostgREST 是社区老项目,比 Supabase 自己早出生好几年,imgproxy 是独立开发者的作品。连认证服务 GoTrue 都不是从零写的,它原本是 Netlify 开源的项目,Supabase fork 过来一直养到现在,后来改名成了 supabase/auth。
说真的,这就是 Supabase 的 README 里那句最硬核的宣言,我原文抄给你。
If the tools and communities exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool. If the tool doesn't exist, we build and open source it ourselves.
有现成的就拿来用,没有的自己造再开源出去。realtime、supavisor、edge-runtime 就是「没有所以自己造」的那部分。
一张表是怎么变成带权限的 API 的
拆完架构再拆机制。Supabase 最让人困惑的一点是,我没有写后端代码,为什么 API 自带权限?答案藏在 JWT 和 RLS 的配合里,整个链路值得单独画出来。
用户登录,auth 服务签发一个 JWT,里面的 claims 带着用户 id。之后每次请求,客户端把这个 JWT 放在 header 里发给网关。Envoy 按路径分流,/rest/v1 开头的扔给 PostgREST。PostgREST 干两件事,先验证 JWT 签名,再把 JWT 里的 claims 映射成数据库能理解的角色信息,比如 request.jwt.claims。最后 SQL 到达 Postgres,RLS 行级安全策略接手,auth.uid() 这种 helper 函数拿着 claims 里的用户 id 去比对当前行的 owner 字段,对不上就过滤掉。
所以你在 supabase-js 里写的那句 .from('todos').select(),落到底层是一条带角色上下文的 SQL,权限不是在应用层判的,是 Postgres 在行级别判的。
这个设计的代价是权限逻辑全部押在 RLS 上,一旦策略写漏一行,就是数据越权。他们的解法也挺有意思,仪表盘里直接内置了一个 AI 功能帮你写 RLS 策略,代码就在这个仓库的 packages/ai-commands 里,README 写得很直白,这个包装着所有涉及 AI 和 LLM 的功能,其中有个函数叫 chatRlsPolicy,专门聊天式生成 RLS 策略,而且流式函数只能在 Edge runtime 上跑,得从 ai-commands/edge 子路径导入。
一个 2019 年立项的数据库工具,把「用 LLM 补齐权限策略这种容易写错的东西」做进了产品层,这个细节比官宣里任何 AI 战略都更能说明团队对 AI 的态度。
Kong 退场,Envoy 接棒,但名字没删干净
翻 compose 文件还能挖出一段迁移史。很多老教程会告诉你 Supabase 自托管网关是 Kong,现在这个认知过时了。docker/README.md 写得明确,Envoy 是默认网关,Kong 降级成可选配置,想切回去得执行 sh run.sh config add kong。
迁移做得很小心。compose 文件第 76 到 80 行,Envoy 容器给自己保留了 kong 这个网络别名,注释写着这样内部配置就不用跟着改。连环境变量都还是 KONG_HTTP_PORT,默认端口 8000 沿用至今。
但我抓到一个文档漂移。docker/versions.md 记录镜像版本变更,2026 年 8 月 3 日那条还在记 kong/kong:3.9.3 的版本升级,可默认 compose 里早就没有 Kong 这个服务了,真正在跑的是 envoy v1.39.0。讲真,给一个已经不在默认栈里的服务记版本账,大概率是自动化脚本顺手提交的。小事,但说明这个仓库的文档密度已经大到官方自己都顾不过来。
同一个月的记录里还有条更重要的,2026 年 6 月 17 日,自托管 Postgres 从 15.8.1.085 一步跳到 17.6.1.136。云平台先行,自托管跟进,大版本跨了两代。
这个仓库里全是 AI 痕迹
前面说的 ai-commands 是产品里的 AI,仓库本身的开发工作流更是全副武装的 Agent 化。
根目录的 .mcp.json 里配了一个 MCP 服务器,指向 https://mcp.supabase.com/mcp,也就是说你用 Claude Code 或 Cursor 打开这个仓库,会自动挂上官方托管的 Supabase MCP。.claude 目录里躺着 CLAUDE.md、settings.json、scripts、skills 四件套,.agents 目录里还有一套 skills。CI 那边挂着 .coderabbit.yaml 让 AI 审 PR,zizmor.yml 管 GitHub Actions 的安全审计。
你猜怎么着,issue 区最能体现自动化失控的样子。排名第 2 的高评论 issue,#44860,46 条评论,标题是「Changes by create-pull-request action」,一个机器人自动提交的变更单炸出几十条讨论。这套全自动流水线跑起来是挺爽,翻车的时候也是真热闹。
自托管的账要算清楚
批判环节我不想单列一章念清单,跟着使用场景走更实在。
第一个账是功能差异。云版 Supabase 有完整的日志分析,但默认的 docker-compose.yml 里没有任何 analytics 服务,logflare 1.43.1 和日志采集器 timberio/vector 0.53.0 被挪进了 docker-compose.logs.yml 这个可选变体里。compose 主文件里只留下两行注释,提了一句内部 _analytics 数据库需要改动。云上有的,自托管默认没有,这个 gap 官方不会写在营销页上。
第二个账是老问题的时间纵深。issue #6435,请求「SMS OTP 模板支持换行」,68 条评论,从提出到现在挂了很多年还没关闭。短信验证码模板里不能换行,这种细节级痛点最能反映一个产品的工单优先级。另一个 #43662,MCP OAuth 在 Cursor 里报 Unrecognized client_id,29 条评论,正好卡在他们力推 AI 工作流的当口。
第三个账是人。看贡献者列表,前四名 joshenlim 4894 次提交、kiwicopple 3419 次(联合创始人 Paul Copplestone)、MildTomato 2942 次、saltcod 2504 次,全是员工。核心栈按公司节奏走,社区贡献主要落在文档和客户端库。开源的壳,商业的核,bus factor 是一家公司而不是一个社区。这个模式没什么不对,但你要清楚自己依赖的是什么。
再往前看一步,2026 年 9 月 15 日,PR #49020 会把 Multigres 的公测文档合入,这是 Supabase 自研的多节点 Postgres 高可用方案,文档侧边栏里排在 OrioleDB 之后。从组合开源件,到 fork Netlify 的认证服务,再到自己做 Postgres 的 HA 层,这家公司的自研比例在肉眼可见地上升。拼装起家,慢慢长出自己的引擎,这个路径值得所有做开源商业化的团队琢磨。
拆完之后我带走了一个模式
我一直觉得拆这种仓库最大的收获不是又认识了几个工具,而是「门面仓库」这个组织方式本身。
引擎分散在 postgrest、supabase/auth、supabase/realtime 各自的仓库里独立演进,主仓库只装用户看得见的东西,仪表盘、文档、营销站、自托管发行版。组合方式写在 compose 文件里而不是代码里,好处是每一层都能单独升级、单独替换、单独退出。用户拿到的不是一个必须整体信仰的单体,而是一桌可以单点退菜的席。
落到选型上,这个结构给了一个比功能清单硬得多的判断标准,看逃生通道。我拉个表你感受下。
| 项目 | 引擎来源 | 自托管 | 逃生通道 |
|---|---|---|---|
| Supabase | Postgres 生态开源件组合 | 官方 compose,13 服务 | pg_dump 或直连连接串,随时搬走 |
| Firebase | Google 闭源全家桶 | 无 | 只能导 JSON |
| Appwrite | 自研微服务单体 | 有 | 无标准 SQL 引擎可迁 |
| PocketBase | Go 单二进制内嵌 SQLite | 一个文件跑起来 | SQLite 文件直接拖走 |
| Convex | 自研 TS 运行时 | 有,能力受限 | 数据格式私有 |
下次选后端平台,别问功能够不够,先问数据走不走得了。Supabase 把每一层都押在 Postgres 生态上,等于把「供应商锁定」这个 BaaS 最大的风险项做成了可逆操作,哪怕明天云平台关停,compose 文件拉起来,13 个容器照样转。
能白嫖的绝不自研,必须自研的立刻开源。这个 2019 年写在 README 里的策略,七年后回看,可能是整个项目最值钱的一行字。

评论互动