如何用 Docker 在 VPS 上自托管 Supabase
了解如何运行官方 Supabase Docker 堆栈:必须替换的演示密钥、约 14 个服务的作用、为何建议预留 8 GB 内存,以及安全备份和更新方法。
您要构建的内容
自行托管 Supabase,意味着在您自己的服务器上运行官方 Docker Compose 堆栈:Postgres、前置 REST API、身份验证服务、文件存储、实时 WebSocket,以及 Studio 控制面板。您只需克隆一个仓库,编辑一个 .env 文件,然后启动大约 14 个容器。这些容器协同运行,其行为类似于一个由您控制的 Supabase 项目。
安装过程很短。最容易出问题的是 .env 文件。该文件包含仓库中公开发布的演示密钥,使用这些默认值启动的堆栈会向任何找到它的人开放。本指南介绍必须替换的密钥、各服务的用途、堆栈实际需要多少内存,以及如何在不删除数据库的情况下更新堆栈。
如果您不熟悉 Compose,请先阅读 VPS 上的 Docker Compose 基础知识。以下内容均假设 docker compose version 已经可以输出版本信息。
堆栈实际包含的内容
Supabase 不是一个程序。Compose 文件会在同一网络上启动一组独立服务。了解各服务的作用后,才能将一长串容器名称转化为可调试的对象。
db是加载了 Supabase 扩展的 PostgreSQL。其他所有服务都会与它通信。如果此容器运行不正常,其他服务也都会失败。kong是 API 网关。它监听 8000 端口,并将/rest/v1/、/auth/v1/和/storage/v1/路由到正确的后端。只有此容器应对外暴露。rest是 PostgREST。它读取 Postgres 架构并将其作为 REST API 提供,因此新建表后无需编写代码即可获得新的端点。auth是 GoTrue。它签发用于标识用户的 JSON Web Token(JWT)。storage和imgproxy处理文件上传和图像缩放。realtime通过 websockets 流式传输数据库变更。studio和meta分别是仪表板及其背后的管理 API。analytics(Logflare)和vector收集日志,supavisor是 Postgres 连接池。
这就是下面资源数值的由来。您运行的不只是一个数据库,而是一个数据库加上十多个支持服务。
规格规划:预留 8 GB 内存
截至 2026 年 7 月,在全新安装且尚未加入您自己的数据或流量时,此技术栈空闲时约占用 2.5 到 3 GB 常驻内存。分析服务和 Studio Node.js 进程是占用内存最多的两个独立组件。2 GB 服务器会启动这些容器,随后内核的内存不足终止程序通常会终止其中一个,通常为 analytics 或 db。此时的表现是某个容器不断重启,并以退出代码 137 退出。
对于任何依赖的环境,请配置 8 GB 内存和 4 vCPU。如果您能接受重型查询与 Studio 会话同时运行时速度较慢,4 GB 也可用于个人开发实例。磁盘空间同样重要,因为 Postgres、存储卷和日志数据都位于项目目录下。先配置 40 GB,并持续监控磁盘使用情况。
安装:克隆官方仓库
受支持的方式是将主仓库中的 docker 目录复制到您自己的项目目录中。这样分离很重要,因为后续的 git pull 不会覆盖您的 .env。
git clone --depth 1 https://github.com/supabase/supabase
mkdir supabase-project
cp -rf supabase/docker/* supabase-project
cp supabase/docker/.env.example supabase-project/.env
cd supabase-project
docker compose pulldocker compose pull 会下载数 GB 的镜像。完成后,每项服务都应标记为 Pulled。此处出现 manifest unknown 错误,表示上游已移除固定的镜像标签。解决方法是拉取仓库的较新副本,而不是手动编辑标签。
首次启动前必须更改的机密值
请在启动堆栈前完成这些操作,不要等启动后再做。其中一些值会在首次启动时写入数据,因此之后再更改就意味着重置数据库。
代码仓库提供了一个生成器,可正确生成所有值,包括必须使用新的 JWT secret 签名的两个 API key。
sh utils/generate-keys.sh --update-env该脚本会为 JWT_SECRET、ANON_KEY、SERVICE_ROLE_KEY、SECRET_KEY_BASE、REALTIME_DB_ENC_KEY、VAULT_ENC_KEY、PG_META_CRYPTO_KEY 以及 Logflare token 写入新值,目标文件为 .env。它需要 openssl,任何标准 Ubuntu 镜像都已包含该工具。
以下两个值不会自动设置,您必须在 .env 中手动编辑:
POSTGRES_PASSWORD。只能使用字母和数字。此处的标点符号会破坏多个服务通过拼接字符串生成的连接字符串。故障表现为身份验证错误,而不是解析错误,因此容易导致排查方向错误。DASHBOARD_USERNAME和DASHBOARD_PASSWORD。这是 Studio 的基本身份验证凭据。随附的默认密码就是this_password_is_insecure_and_should_be_updated。
请理解为什么不能自行编造 ANON_KEY 和 SERVICE_ROLE_KEY。它们都是使用 JWT_SECRET 签名的 JWT。网关会在每个请求上验证该签名,因此与您的 secret 不匹配的 key 会因 {"message":"Invalid authentication credentials"} 而被拒绝。这是自行托管时最常见的故障:操作员更改了 JWT_SECRET,但仍保留演示 key。始终同时生成这三个值。
请将 SERVICE_ROLE_KEY 视为 root 密码。它可以完全绕过行级安全控制。它只能放在服务器端代码中,不能放在其他位置。
将 SITE_URL 和 API_EXTERNAL_URL 设置为用户实际访问的地址,例如 https://supabase.example.com。Auth 会根据这些值生成电子邮件确认链接和 OAuth 回调链接,因此如果仍保留 http://localhost:8000,每位用户都会被重定向到自己的计算机。
然后检查当前配置:
sh run.sh secrets启动并确认运行正常
sh run.sh start
docker compose psrun.sh start 会封装 docker compose up -d --wait,因此只有健康检查通过后才会返回。每项服务都应显示 running (healthy) 或 running。首次启动需要 2 到 4 分钟,因为 Postgres 会先运行初始化脚本,之后其他服务才能连接。
如果某个容器反复重启,请按服务名称查看其日志:
docker compose logs db
docker compose logs authStudio 随后会使用端口 8000,并要求输入您设置的仪表板用户名和密码。
不要将 port 8000 暴露到公共互联网
Kong 在 8000 上使用明文 HTTP。所有 API key 和用户密码都会以明文形式通过网络传输。Studio 凭据使用 basic authentication,其本质是 base64 编码,而不是加密。
在它前面放置反向代理,并在反向代理处终止 TLS(传输层安全)。同时将 Kong 绑定到 loopback 地址,避免其他主机访问它。在 docker-compose.yml 中,kong 端口映射改为 127.0.0.1:8000:8000,代理再将请求转发到该端口。在多个 Compose 应用前使用 Traefik介绍证书配置。
还应在防火墙中关闭其余端口,因为 Docker 会通过写入自己的 iptables 规则来发布端口,简单的 ufw 配置无法识别这些规则。Docker 容器为何会忽略 ufw 规则解释了这一问题。
备份数据库,而不是目录
Postgres 数据存储在 ./volumes/db/data 的绑定挂载中。容器运行时复制该目录会得到不完整的副本,因为 Postgres 会缓冲写入,磁盘上的文件只有在检查点时才保持一致。恢复该副本通常可以成功,但有时会静默丢失最后几笔事务。这是备份可能出现的最糟糕故障模式。
改用转储。pg_dumpall 在容器内运行,并生成一致的快照:
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sql在信任该文件前,先确认文件不为空。然后按计划将这些转储传输到服务器之外,这正是 使用 restic 的加密异地备份 的用途。同时备份您的 .env。丢失 JWT_SECRET 会使所有已签发的令牌失效,并导致所有已存储的加密机密无法读取。
上传的文件位于 ./volumes/storage 中。这些是普通文件,因此可以直接复制。
更新而不丢失数据
Supabase 在 docker-compose.yml 中固定镜像版本,因此除非手动更新,否则版本不会变化。每次都先创建转储。
docker compose pull
sh run.sh recreaterecreate 会停止堆栈,然后使用新镜像重新启动。由于数据存储在主机上的绑定挂载中,而不在容器内,因此数据不会丢失。进行主要版本升级前,请阅读仓库中的 CHANGELOG.md,因为 Postgres 主要版本升级不会自动执行,需要先创建转储,再进行恢复。
如果要应用 Compose 文件本身的更改,请重新克隆上游仓库,并将其中的 docker 目录复制到项目中,同时注意不要覆盖 .env。
完全重置会删除所有内容,包括数据库。该操作使用单独的脚本,并且会要求确认:
sh reset.shFAQ
为什么我的 API 调用返回“Invalid authentication credentials”?
您的 ANON_KEY 或 SERVICE_ROLE_KEY 未使用当前位于 .env 中的 JWT_SECRET 进行签名。网关会验证每个请求的签名,签名不匹配时会拒绝请求。使用 sh utils/generate-keys.sh --update-env 同时重新生成这三个值,然后运行 sh run.sh recreate,使服务读取新值。
我可以在 2 GB VPS 上运行自托管 Supabase 吗?
不可靠。截至 July 2026,该技术栈空闲时的内存占用接近 3 GB,因为它会运行约 14 个服务。因此,2 GB 服务器上的容器会被 out of memory killer 终止,您会在 docker compose ps 中看到退出代码 137。生产环境使用 8 GB,个人开发最低使用 4 GB。
自托管 Supabase 是否包含 edge functions?
包含。Compose 文件包含基于 Deno 的函数运行时,并会提供您放置在 ./volumes/functions 下的所有内容。它不包含托管平台的全球部署网络,因此您的函数只会在一台服务器上的一个位置运行。
如何直接连接到 Postgres 数据库?
在服务器本机上使用 docker exec -it supabase-db psql -U postgres 打开交互式 shell。对于外部客户端,使用用户 postgres.<POOLER_TENANT_ID> 和您的 POSTGRES_PASSWORD 通过 5432 端口连接到 Supavisor。不要将该端口开放到互联网。请通过 VPN 或 SSH 隧道访问。
为什么我的身份验证确认邮件链接到了 localhost?
.env 中的 SITE_URL 和 API_EXTERNAL_URL 仍使用默认值。身份验证服务会根据这两个值生成所有确认链接和密码重置链接,因此它会发送配置值指定的地址。将两者都设置为真实的公开 URL,然后重新创建技术栈。