我把本地 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”:它们根本不是同一个东西
大多数新手只接触过两个形态,实际上官方生态里至少有三种运行形态,差异远比想象中大:
- 本地 CLI 栈(
supabase start):跑在你自己电脑的 Docker 里,自带本地 SMTP 服务器(Inbucket)和本地 analytics server,日志直接读 Docker 日志驱动。官方文档明确说它拉取的镜像“包括一堆本地开发专用镜像,比如本地 SMTP 服务器”。它甚至连连接池都不跑——docker/CONFIG.md 里白纸黑字写着:“CLI 不运行 Supavisor”。 - 自托管 Docker 栈(docker/docker-compose.yml):官方发布的一套 Compose 编排,包含 Studio、Envoy API 网关、Auth(GoTrue)、PostgREST、Realtime、Storage、imgproxy、postgres-meta、Edge Runtime、Logflare、Vector、Supavisor 等十几个服务。
- 托管版(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),平滑路径大致是:
- 停写或选低峰窗口,先在源库跑三份 dump;
- 在托管版建项目,
supabase link --project-ref <ref>关联; - 先推
roles.sql,再推schema.sql,最后data.sql——顺序错了角色不存在时建表会失败; - Storage 对象用
supabase storage cp或 S3 协议端点同步(docker/.env.example 中S3_PROTOCOL_ACCESS_KEY_ID/SECRET就是为这类工具准备的); - 迁移边缘函数代码,托管版走
supabase functions deploy; - 切换前端的环境变量:
SUPABASE_URL换成托管项目地址,ANON_KEY/service_role换成托管项目的新密钥,清掉一切硬编码的自托管 URL。
3. 迁过去之后你才意识到托管版替你扛了什么
- 备份:托管版有平台级连续备份与 PITR,不需要你自己写 cron +
pg_dump脚本; - 升级:没有每月快照、没有三路合并冲突、没有 PG15→17 手工大手术;
- 性能:Postgres 参数按 compute 自动调优,连接池、监控、日志开箱即用;
- 安全:Dashboard 无需 Basic Auth,密钥轮换在控制台一键完成,HTTPS、域名、TLS 证书全托管。
五、写在最后:别让“免费”两个字绑架你的生产环境
复盘这次“把本地当生产”的惨痛经历,可以沉淀成一份自托管上线自检清单:
- 上线前跑
sh utils/generate-keys.sh和sh utils/add-new-auth-keys.sh,确认.env无占位符、DASHBOARD_PASSWORD已改; - 确认
SUPABASE_PUBLIC_URL/API_EXTERNAL_URL/SITE_URL已指向 HTTPS 域名,反向代理支持 WebSocket; FUNCTIONS_VERIFY_JWT=true,接好生产 SMTP,Storage 别用本地绑定挂载(macOS 上 xattr 与权限问题尤其多);- 建立独立于 update.sh 的数据库与 Storage 备份,并定期演练恢复;
- 升级前先
sh update.sh --dry-run,看清冲突与 breaking-change 再动手; - 按 RAM/CPU 手动调 Postgres 参数,压测连接池水位;
- 评估成本与维护工时:自托管省下的是订阅费,付出的是一次次深夜救火的运维工时。
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),仅供参考