SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-10-11

如何用 Docker 在 VPS 上自托管 Supabase

在自有 VPS 上运行官方 Supabase Docker Compose 堆栈,了解必须替换的默认密钥、11 个服务的用途、实际内存需求,以及备份和更新方法。

构建内容

自行托管 Supabase,意味着在自己的服务器上运行官方 Docker Compose 堆栈:Postgres、前置 REST API、身份验证服务、文件存储、实时 WebSocket,以及 Studio 控制面板。您只需克隆一个仓库、编辑一个 .env 文件,然后启动 11 个容器。这些容器共同提供一个由您控制的 Supabase 项目。

安装过程很短。最容易出问题的是 .env 文件。该文件自带仓库中公开的演示密钥,使用这些默认值启动的堆栈对任何发现它的人开放。本指南介绍必须替换的密钥、各服务的用途、该堆栈实际需要多少内存,以及如何在不删除数据库的情况下更新它。

如果您不熟悉 Compose,请先阅读 VPS 上的 Docker Compose 基础知识。以下内容假设 docker compose version 已能输出版本号。

实际包含哪些服务

Supabase 不是单个程序。Compose 文件会在同一个网络中启动一组独立服务。了解每个服务的作用后,面对一长串容器名称时,才能进行排查。

  • db 是加载了 Supabase 扩展的 PostgreSQL。其他所有服务都会与它通信。如果此容器运行不正常,其他服务也会全部失败。
  • api-gw 是 API 网关。自 August 2026 版本起,它使用 Envoy;在此之前使用名为 kong 的服务。Kong 仍可通过可选覆盖文件启用。它监听 8000 端口,并将 /rest/v1/、/auth/v1/ 和 /storage/v1/ 路由到正确的后端。只有这个容器应对外暴露。
  • rest 是 PostgREST。它读取 Postgres 架构并将其作为 REST API 提供,因此新建表后无需编写代码就会产生新的端点。
  • auth 是 GoTrue。它签发用于标识用户的 JSON Web Token(JWT)。
  • storage 和 imgproxy 负责文件上传和图像缩放。
  • realtime 通过 WebSocket 流式传输数据库变更。
  • studio 和 meta 分别是控制面板及其背后的管理 API。
  • functions 是基于 Deno 的边缘运行时,supavisor 是 Postgres 连接池器。

日志和分析服务(Logflare 加 vector)已于 June 2026 从默认 Compose 文件中移除。现在它们位于可选覆盖文件中。使用 sh run.sh config add logs 启用该文件。Compose 文件的这种分层方式与使用多个 Compose 文件运行一个服务栈中介绍的方式相同。

这就是下面的资源数量如此设置的原因。你运行的不只是一个数据库,而是一个数据库加上十个支持服务。

规划 8 GB 内存

截至 2026 年 7 月,在全新安装且尚未加入您自己的数据或流量时,该堆栈的常驻内存占用约为 2.5 到 3 GB。在这次测量中,Analytics 服务和 Studio 的 Node.js 进程是两个占用内存最多的单独进程。Analytics 已不再属于默认堆栈,因此只有启用日志覆盖配置时才会占用内存。2 GB 的服务器可以启动这些容器,但随后会有容器被内核的 OOM killer 终止。表现为容器不断重启,并以退出码 137 退出。Supabase 自托管文档在 2026 年 10 月核查时列出的最低配置为 4 GB RAM、2 个 CPU 核心和 40 GB SSD;推荐配置为 8 GB RAM、4 个核心和 80 GB SSD。

对于任何您依赖的服务,请提供 8 GB RAM 和 4 个 vCPU。如果您能接受同时执行大型查询和使用 Studio 时速度较慢,4 GB 也可用于个人开发实例。磁盘同样重要,因为 Postgres、存储卷以及启用日志后产生的日志数据都位于项目目录下。先从 40 GB 开始,并持续监控磁盘使用情况。在选择方案前统计服务数量,是自托管任何服务时都值得保持的习惯,因为 PhotoPrism 和 Immich 也有明确的内存下限:Immich 的快速入门页面在 2026 年 10 月核查时要求至少 6 GB RAM 和 2 个 CPU 核心。如果目前项目还不需要这么高的服务器配置,请将其与 Supabase 免费云方案实际包含的内容进行比较,因为一个内存占用低于 500 MB 且一周内从不处于空闲状态的小型应用,可能可以免费运行在该方案上。

安装:克隆官方仓库

受支持的方式是将主仓库中的 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 pull

docker compose pull 会下载数 GB 的镜像。命令结束时,每个服务都应标记为 Pulled。此处出现 manifest unknown 错误,表示上游已删除固定的镜像标签。解决方法是重新拉取仓库的较新副本,而不是手动修改标签。

首次启动前必须更改的密钥

请在启动堆栈前完成这些操作,不要等启动后再做。部分值会在首次启动时写入数据,因此之后修改就意味着重置数据库。

该仓库提供了一个生成器,可正确生成所有值,包括必须使用新的 JWT 密钥签名的两个 API 密钥。

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 令牌、S3 协议密钥和 MINIO_ROOT_PASSWORD 写入 .env。截至 October 2026,它还会为 POSTGRES_PASSWORD 和 DASHBOARD_PASSWORD 写入随机值。该脚本需要 openssl;任何标准 Ubuntu 镜像都包含此工具。由于脚本现在也会重写数据库密码,因此只能在首次启动前使用 --update-env,绝不能对已经包含数据的堆栈执行。

.env 中仍有两个值值得手动检查:

  • POSTGRES_PASSWORD。脚本会为其填入随机十六进制值,这是安全的。如果改用自定义值,只能使用字母和数字。此处的标点符号会破坏多个服务通过拼接字符串生成的连接字符串。故障表现为身份验证错误,而不是解析错误,容易导致排查方向错误。
  • DASHBOARD_USERNAME 和 DASHBOARD_PASSWORD。这两个值是 Studio 的基本身份验证凭据。脚本会设置随机密码,但会将用户名保留为默认值 supabase。随附的默认密码就是 this_password_is_insecure_and_should_be_updated;如果跳过了脚本,现在就修改该密码。

请理解为什么不能自行编造 ANON_KEY 和 SERVICE_ROLE_KEY。两者都是使用 JWT_SECRET 签名的 JWT。网关只接受 .env 中的准确密钥,后端服务会根据 JWT_SECRET 验证签名,因此不匹配的密钥会返回 HTTP 401。默认 Envoy 网关会响应 401 Unauthorized,当前的 Kong 覆盖配置也会响应 {"message":"Unauthorized"};该配置运行 Kong 3.9。2026 年 3 月之前的安装运行 Kong 2.8,会响应 {"message":"Invalid authentication credentials"}。这是自行托管时最常见的故障:运维人员修改了 JWT_SECRET,却保留了演示密钥。始终同时生成这三个值。

请将 SERVICE_ROLE_KEY 按照 root 密码处理。它会完全绕过行级安全策略。它只能放在服务器端代码中,不能放在其他位置。

将 SITE_URL 设置为应用的地址,将 API_EXTERNAL_URL 设置为 API 的公网地址,并在末尾加上 /auth/v1,例如 https://supabase.example.com/auth/v1。将 SUPABASE_PUBLIC_URL 设置为不带路径的同一公网地址。Auth 会根据这些值生成电子邮件确认链接和 OAuth 回调链接,因此保留其 localhost 默认值会将所有用户重定向到各自的本机。

然后检查配置:

sh run.sh secrets

启动并确认服务运行正常

sh run.sh start
docker compose ps

run.sh start 会封装 docker compose up -d --wait,因此只有健康检查通过后才会返回。每个服务都应显示 running (healthy) 或 running。首次启动需要 2 到 4 分钟,因为 Postgres 会先运行初始化脚本,之后其他服务才能连接。

如果容器不断重启,请按服务名称查看其日志:

docker compose logs db
docker compose logs auth

Studio 随后会监听 8000 端口,并要求输入您设置的仪表板用户名和密码。

不要将端口 8000 暴露到公网

8000 端口上的网关使用明文 HTTP。每个 API 密钥和用户密码都会以明文在网络中传输;Studio 凭据使用基本身份验证,其本质是 Base64 编码,而不是加密。

在网关前配置反向代理,并在代理层终止 TLS(传输层安全)。同时将网关绑定到回环地址,使其他主机无法访问它。在 docker-compose.yml 中,api-gw 服务下的端口配置改为 127.0.0.1:${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp,然后让代理转发到该端口。在 2026 年 8 月切换到 Envoy 之前安装的版本中,同一行端口配置位于名为 kong 的服务下。在多个 Compose 应用前配置 Traefik介绍了证书配置。该代理最终还会为此主机上的其他服务提供入口,从当前堆栈到像将 Jellyfin 媒体库重建为 90 年代录像店这类非正式服务;这些服务都应使用主机名,而不是继续开放新的端口。如果某个仪表板只供你自己使用,可以跳过代理,改用 SSH 隧道访问回环端口。self-hosted open-kritt也采用这种方式,使其扫描界面完全不暴露到公网。

还应在防火墙中关闭其余端口,因为 Docker 会通过写入自己的 iptables 规则来发布端口,简单的 ufw 配置无法识别这些规则。为什么 Docker 容器会忽略你的 ufw 规则解释了这一问题。

备份数据库,而不是目录

Postgres 数据存储在 ./volumes/db/data 的 bind mount 中。容器运行时复制该目录会得到不完整的副本,因为 Postgres 会缓冲写入,磁盘上的文件只有在检查点时才处于一致状态。恢复该副本通常可以成功,但有时会悄悄丢失最后几笔事务。这是备份最糟糕的失败方式。

改用转储。pg_dumpall 在容器内运行,并生成一致的快照:

docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sql

确认文件非空后再信任它。然后按计划将这些转储发送到服务器之外,这正是 使用 restic 加密备份到异地 的用途。静默失败的定时转储与完全没有备份无法区分,因此应让 cron 或 systemd 任务在退出码非零时 向您的手机发送告警。同时备份您的 .env。丢失 JWT_SECRET 会使所有已签发的令牌失效,并使所有存储的加密密钥无法读取。

上传的文件位于 ./volumes/storage 中,这些是普通文件,因此直接复制即可。

无损更新

Supabase 会在 docker-compose.yml 中固定镜像版本,因此除非手动更新,否则不会发生变化。无论手动组装哪种技术栈,都建议固定版本。这也是自托管 RustDesk 中继固定两个服务器镜像、而不是跟随浮动标签的原因:升级应当安排在您有充足时间处理的时段。每次升级前都必须先执行转储。

docker compose pull
sh run.sh recreate

recreate 会停止技术栈,然后使用新镜像重新启动。数据不会丢失,因为数据位于主机上的绑定挂载中,而不在容器内部。进行主要版本升级前,请先阅读仓库中的 CHANGELOG.md,因为 Postgres 主要版本升级是单独的步骤,recreate 不会执行该步骤。自 June 2026 起,Postgres 17 一直是默认版本;现有的 Postgres 15 安装可通过 sudo bash utils/upgrade-pg17.sh 升级到该版本。该命令使用 pg_upgrade 就地迁移数据,并在每个主要步骤中请求确认。执行前必须确保所有容器都在运行,并且可用磁盘空间至少为数据库大小的两倍再加 5 GB。Supabase 仍建议您在开始前自行创建独立备份。

如果要应用 Compose 文件本身的变更,请在项目目录中运行 sh update.sh;也可以先运行 sh update.sh --dry-run 查看变更内容。该命令会将上游文件与您的修改合并,绝不会覆盖 .env 中已有的值(只会追加新键),并会在写入前将配置归档到 backups/,但不会归档数据库。没有 .supabase-version 文件的安装需要使用 --from,并指定安装时所用的版本。对于 August 2026 版本,这一点尤其重要,因为该版本将 kong 服务替换为 api-gw,并要求更新 docker-compose.yml。手动操作仍然可用:再次克隆上游仓库,然后将其中的 docker 目录复制到项目中,但不要覆盖 .env。

完整重置会销毁所有内容,包括数据库,并将 .env 恢复为默认值(旧文件会保留为 .env.old)。这是一个独立脚本,执行时会请求确认:

sh reset.sh

FAQ

为什么我的 API 调用返回“Invalid authentication credentials”?

这条确切消息来自 Kong 2.8,它是 2026 年 3 月之前安装的堆栈中的网关。默认 Envoy 网关和当前的 Kong 覆盖配置使用 Kong 3.9,遇到同一问题时会返回普通的 401 Unauthorized。无论哪种情况,您发送的密钥都与 .env 中的 ANON_KEY 或 SERVICE_ROLE_KEY 不匹配,或者这些密钥不是使用 .env 中当前的 JWT_SECRET 签名的。对于从未存储过数据的堆栈,使用 sh utils/generate-keys.sh --update-env 一起重新生成这三个值。对于正在运行的堆栈,不带该标志运行脚本并回答 no,使脚本只打印新值;手动将 JWT_SECRET、ANON_KEY 和 SERVICE_ROLE_KEY 复制到 .env,并保持 POSTGRES_PASSWORD 不变。然后运行 sh run.sh recreate,使服务读取新值。

我可以在 2 GB VPS 上运行自托管 Supabase 吗?

不能可靠运行。截至 2026 年 10 月,Supabase 的自托管文档将 4 GB RAM 列为最低要求;截至 2026 年 7 月,在一项包含日志服务的测量中,该堆栈空闲时接近 3 GB,而日志服务现在需要选择启用。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。对于外部客户端,通过 5432 端口上的 Supavisor 连接,并使用用户 postgres.<POOLER_TENANT_ID> 和您的 POSTGRES_PASSWORD。不要将该端口开放到互联网。请通过 VPN 或 SSH 隧道访问它。

为什么我的身份验证确认邮件链接到了 localhost?

.env 中的 SITE_URL 和 API_EXTERNAL_URL 保留了默认值。身份验证服务会根据这两个值生成所有确认链接和密码重置链接,因此它会发送配置中指定的地址。将二者都设置为真实的公网 URL,然后重新创建堆栈。

这篇文章对您有帮助吗?