☰
我把本地 Supabase 当生产环境用,然后哭了:自托管踩坑实录
2026/10/11 15:04:10 网站建设 项目流程

我把本地 Supabase 当生产环境用,然后哭了:自托管踩坑实录

【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase

“本地supabase start跑起来和托管版一模一样,部署时直接照搬就行”——如果你也这么想过,请先看完这篇文章再动手。Supabase 确实用一套开源技术栈把 Auth、PostgREST、Realtime、Storage、Edge Functions 打包得严丝合缝,但“能跑”和“能上线”之间隔着一整条运维鸿沟。本文结合官方自托管仓库(docker/README.md、docker/CONFIG.md)与官方运维文档的源码级细节,把本地环境、自托管 Docker 栈与托管版三者之间的真实差异、备份升级与性能调优的连环坑,以及迁回托管版的平滑路径一次讲透。

一、先分清三个“Supabase”:它们根本不是同一个东西

大多数新手只接触过两个形态,实际上官方生态里至少有三种运行形态,差异远比想象中大:

  1. 本地 CLI 栈(supabase start):跑在你自己电脑的 Docker 里,自带本地 SMTP 服务器(Inbucket)和本地 analytics server,日志直接读 Docker 日志驱动。官方文档明确说它拉取的镜像“包括一堆本地开发专用镜像,比如本地 SMTP 服务器”。它甚至连连接池都不跑——docker/CONFIG.md 里白纸黑字写着:“CLI 不运行 Supavisor”。
  2. 自托管 Docker 栈(docker/docker-compose.yml):官方发布的一套 Compose 编排,包含 Studio、Envoy API 网关、Auth(GoTrue)、PostgREST、Realtime、Storage、imgproxy、postgres-meta、Edge Runtime、Logflare、Vector、Supavisor 等十几个服务。
  3. 托管版(supabase.com 云平台):由官方运维,自动备份、自动升级、自动调优、多可用区。

三者共享同一套产品代码,但运维责任完全不同。很多人把本地 CLI 栈直接当成生产环境,或者是把自托管 Docker 栈当成“云平台的免费复制品”,结果在备份、升级、性能三个方向上连环踩坑。

二、差异清单:从“能跑”到“能上线”的每一道坎

1. 默认配置从第一天起就是“不安全”的

这是自托管仓库 README 里最醒目的一行警告:

⚠️The default configuration is not secure for production use.

docker/README.md 的 “Important Notes” 一节列出上线前必须完成的清单:更换.env里所有默认密码和密钥、检查 CORS 配置、在 API 网关前加安全反向代理、调整网络 ACL、建立正式备份流程。

打开 docker/.env.example,你会看到一排惊悚的占位符:POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password、JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters-long、DASHBOARD_PASSWORD=this_password_is_insecure_and_should_be_updated,还有两枚硬编码在示例文件里的 HS256 签名 ANON_KEY 和 SERVICE_ROLE_KEY——任何没换密钥就上线的实例,等于把数据库大门钥匙挂在门口。文件头部甚至直接写着:“启动容器之前你必须改掉下面所有默认值”。

仓库还为此提供了生成工具:sh utils/generate-keys.sh生成随机密码与密钥,sh utils/add-new-auth-keys.sh生成不对称签名密钥对与新版 API Key。这里的细节很关键:自托管从JWT_SECRET(对称 HS256)迁移到JWT_JWKS/SUPABASE_PUBLISHABLE_KEY/SUPABASE_SECRET_KEY(ES256 不对称 + 不透明密钥)的机制在 docker/docker-compose.yml 的 auth、rest、realtime、storage 四段中都有体现——PostgREST 用PGRST_JWT_SECRET: ${JWT_JWKS:-${JWT_SECRET}},Realtime 用API_JWT_SECRET,Storage 用AUTH_JWT_SECRET。升级密钥体系时漏改任何一个服务,都会出现“认证能登录、API 却 401”的诡异现象。

2. Studio 只有 HTTP Basic 认证,HTTPS 是硬性要求

托管版的 Dashboard 有完整的平台级鉴权体系,而自托管的 Studio 由 API 网关用DASHBOARD_USERNAME/DASHBOARD_PASSWORD做HTTP Basic 认证(见 docker/.env.example 和官方文档)。这意味着:不配 HTTPS 上线,你的管理员口令就是在公网明文裸奔。

官方在 apps/docs/content/guides/self-hosting/self-hosted-proxy-https.mdx 里说得非常直接:“HTTPS is required for production self-hosted Supabase deployments”——尤其接 OAuth 提供商时,自签证书会被绝大多数 OAuth 提供商直接拒绝。同时必须保证反向代理支持 WebSocket(Realtime 依赖)、透传X-Forwarded头,并同步更新.env里的SUPABASE_PUBLIC_URL、API_EXTERNAL_URL、SITE_URL三个 URL。

3. 默认没有日志和分析,出问题只能靠docker logs

托管版的控制台自带 Log Explorer。而自托管默认配置不包含 Logs & Analytics——这是官方文档里明确写出的设计取舍:“默认配置不包含 Logs & Analytics,以减少内存占用”。你要运行sh run.sh config add logs才会叠加 docker/docker-compose.logs.yml,启动 Logflare(分析)与 Vector(日志采集)两个额外容器,而它们会显著推高资源占用。

更隐蔽的是,Postgres 的log_min_messages被设为fatal级别(官方文档注释:为了屏蔽 Realtime 产生的冗余日志)。也就是说,生产环境你默认连数据库的 WARNING/ERROR 日志都看不到,排查慢查询和权限错误时全靠自己调级别。

4. 边缘函数默认不校验 JWT

docker/.env.example 中:FUNCTIONS_VERIFY_JWT=false。托管版边缘函数默认强制校验 JWT,而自托管默认所有函数对拥有 API Key 的调用者开放。对生产环境而言,这是个必须意识到的权限默认值差异——上线前把它改为true,否则任何拿到 anon key 的人都能触发你的边缘函数。

5. 资源门槛与架构复杂度

官方文档给出的系统要求:最小 4GB RAM / 2 核 / 40GB SSD,推荐 8GB+ / 4 核+ / 80GB SSD(apps/docs/content/guides/self-hosting/docker.mdx)。而托管版按 compute add-on 弹性伸缩、自动调优。

整个自托管栈的架构图如下,API 网关(Envoy)站在最前面,背后是 7 个业务服务共用一个 Postgres 实例,连接全部经过 Supavisor 池化:

注意这张图里每个服务都有独立的镜像版本号。docker/versions.md 显示同一批快照里 10 个服务的镜像 tag 各不相同(如supabase/studio:2026.09.07-sha-7996410、supabase/gotrue:v2.196.0、postgrest/postgrest:v14.17、supabase/supavisor:2.9.12)。官方只保证同批快照镜像的相互兼容,你一旦自己混搭版本,兼容性“不保证”——这也是升级坑的源头之一。

三、备份、升级、性能:连环三坑

坑一:update.sh 只备份配置,不备份数据

这是最容易被忽略的一条。官方升级指南 apps/docs/content/guides/self-hosting/updating.mdx 用 Danger 级别的警示框写明:

update.shbacks up your configuration files tobackups/,but it does not back up Postgres or Storage data. Back those up separately first.

也就是说升级脚本的“备份”仅覆盖 Compose 文件、脚本和.env.example,你的数据库和 Storage 文件完全不在保护范围内。许多人是跑完sh update.sh才发现备份目录里根本没有数据,此时数据库若已损坏,只能靠外部备份恢复。

配套的还有 docker/reset.sh 这个“拆家脚本”:它会docker compose down -v --remove-orphans删除 Docker 托管卷,并直接删除./volumes/db/data和./volumes/storage两个目录。它确实会先备份.env到.env.old,但数据库数据目录没有任何备份。生产机上手滑执行一次sh reset.sh,等于一键格式化。

坑二:升级是一场三路合并,冲突会直接写进你的文件

官方采用“每月发布一次稳定快照”的节奏(“We publish stable snapshots of the Docker Compose setup approximately once a month”),update.sh基于.supabase-version记录的基础版本做3-way merge:base(你出发的版本)、new(目标版本)、yours(你本地的改动)三棵树的对比。你没改过的文件干净更新;你改过而新版没碰的行保留你的改动;两边都改了同一行才会产生冲突——此时脚本会把<<<<<<</=======/>>>>>>>合并标记直接写进文件并退出状态码 2,需要你手工打开文件解决。

更麻烦的是“无版本记录”的老部署:官方明确说 update.sh 需要.supabase-version,没有它就不是“一键升级”,需要手工补记录。而升级前它还会读取 docker/upgrades.json 里的 breaking-change 清单,遇到破坏性变更(比如 Postgres 大版本升级)会在改动任何文件前停下来问你确认。

坑三:Postgres 大版本升级要 root、要磁盘翻倍

2026 年 6 月自托管栈把默认 Postgres 从 15 升到了 17(docker/versions.md 记录supabase/postgres:17.6.1.136 (prev 15.8.1.085))。如果你是从 PG15 部署一路升级上来的,必须手动跑 docker/utils/upgrade-pg17.sh:

  • 要求root/sudo、bash、docker compose;
  • 磁盘空间要求至少 2 倍当前数据库大小 + 5GB 空闲;
  • 内部用 Supabase 的pg_upgrade脚本在临时 PG15 容器里完成升级,再把数据目录切换到 PG17;
  • 原 PG15 数据目录会保留为./volumes/db/data.bak.pg15,升级确认成功前千万别删;
  • 失败回滚是一套 5 步手工操作:down、删新数据目录、mv回备份、chown、up。

这还没算docker-compose.pg17.yml这个 override 的切换,以及 PG15/PG17 两个 Compose 文件的共存问题。托管版呢?平台负责升级,你只需在控制台点一下。

坑四:性能调优全部手动,托管版是“出厂自带”

apps/docs/content/guides/ai/going-to-prod.mdx 第 91 行有一句很实在的话:

The Supabase managed platform will automatically optimize Postgres configs for you based on your compute add-on.But if you self-host, consider adjusting your Postgres config based on RAM & CPU cores.

托管版会根据 compute add-on 自动调shared_buffers、work_mem等参数,自托管则全凭自觉。实践中常见的性能坑包括:

  • 连接池参数:默认POOLER_DEFAULT_POOL_SIZE=20、POOLER_MAX_CLIENT_CONN=100(docker/.env.example)。并发上去了之后,Postgres 的max_connections和池大小不匹配,就会出现“明明有流量却报 connection limit exceeded”。
  • 会话模式 vs 事务模式:Supavisor 在 5432 端口提供 session 模式(支持SET、LISTEN/NOTIFY、prepared statements),6543 端口是事务模式(serverless 短连接场景,不支持会话级特性,且 Supavisor 的池化不支持 prepared statements)。选错端口,Realtime 订阅或 ORM 的长连接直接出幺蛾子。
  • 连接字符串的隐藏参数:Supavisor 认证要求用户名带 tenant ID(postgres.your-tenant-id),用psql -U时很容易写漏,导致认证反复失败。
  • 索引与预热:going-to-prod 指南建议上线前用pg_prewarm预热、跑 1 万到 5 万条 warm-up 查询,HNSW 的m/ef_construction参数需要针对自己的 workload 压测。托管版这些事有官方兜底,自托管全是你的活。

四、迁回托管版:一条平滑但必须走对顺序的路

当你被备份、升级、调优三座大山压垮,决定迁回托管版时,好消息是官方提供了一条有工具链支撑的迁移路径,核心思想是:用supabase db dump而非裸pg_dump。

1. 数据库迁移:三份 dump,别用裸 pg_dump

官方指南 apps/docs/content/guides/self-hosting/restore-from-platform.mdx(从平台恢复到自托管)与 CLI 工具链的迁移流程互为镜像。关键命令是三段式:

supabase db dump --db-url "[连接串]" -f roles.sql --role-only supabase db dump --db-url "[连接串]" -f schema.sql supabase db dump --db-url "[连接串]" -f data.sql --use-copy --data-only

文档特别强调:supabase db dump底层调用pg_dump,但会过滤内部 schema、剔除保留角色、加上幂等的IF NOT EXISTS子句;直接用裸pg_dump会把 Supabase 内部对象一起导出来,恢复时产生大量权限错误。这是双向迁移都要遵守的铁律。

2. 从自托管迁到托管版的操作顺序

结合 CLI 工具链(supabase link+supabase db push),平滑路径大致是:

  1. 停写或选低峰窗口,先在源库跑三份 dump;
  2. 在托管版建项目,supabase link --project-ref <ref>关联;
  3. 先推roles.sql,再推schema.sql,最后data.sql——顺序错了角色不存在时建表会失败;
  4. Storage 对象用supabase storage cp或 S3 协议端点同步(docker/.env.example 中S3_PROTOCOL_ACCESS_KEY_ID/SECRET就是为这类工具准备的);
  5. 迁移边缘函数代码,托管版走supabase functions deploy;
  6. 切换前端的环境变量:SUPABASE_URL换成托管项目地址,ANON_KEY/service_role换成托管项目的新密钥,清掉一切硬编码的自托管 URL。

3. 迁过去之后你才意识到托管版替你扛了什么

  • 备份:托管版有平台级连续备份与 PITR,不需要你自己写 cron +pg_dump脚本;
  • 升级:没有每月快照、没有三路合并冲突、没有 PG15→17 手工大手术;
  • 性能:Postgres 参数按 compute 自动调优,连接池、监控、日志开箱即用;
  • 安全:Dashboard 无需 Basic Auth,密钥轮换在控制台一键完成,HTTPS、域名、TLS 证书全托管。

五、写在最后:别让“免费”两个字绑架你的生产环境

复盘这次“把本地当生产”的惨痛经历,可以沉淀成一份自托管上线自检清单:

  1. 上线前跑sh utils/generate-keys.sh和sh utils/add-new-auth-keys.sh,确认.env无占位符、DASHBOARD_PASSWORD已改;
  2. 确认SUPABASE_PUBLIC_URL/API_EXTERNAL_URL/SITE_URL已指向 HTTPS 域名,反向代理支持 WebSocket;
  3. FUNCTIONS_VERIFY_JWT=true,接好生产 SMTP,Storage 别用本地绑定挂载(macOS 上 xattr 与权限问题尤其多);
  4. 建立独立于 update.sh 的数据库与 Storage 备份,并定期演练恢复;
  5. 升级前先sh update.sh --dry-run,看清冲突与 breaking-change 再动手;
  6. 按 RAM/CPU 手动调 Postgres 参数,压测连接池水位;
  7. 评估成本与维护工时:自托管省下的是订阅费,付出的是一次次深夜救火的运维工时。

Supabase 自托管栈的价值在于可控与数据主权——它是把好刀,但刀口永远朝向你。如果你没有专职的 Postgres DBA 和 on-call 排班,托管版才是那个“默认值”。工具链(docker/setup.sh、docker/update.sh、docker/run.sh)已经替你铺好了两条路,选哪条,取决于你打算为自己的数据承担多少运维责任。

【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询