运行多个本地项目
Run a local Supabase project for every app, git worktree, or environment on one machine
本指南说明了如何使用实验性的 supabase stack 命令在一台机器上运行多个本地 Supabase 项目。当你在不同应用之间切换、在多个 git 工作树中工作、并行运行编码代理,或者想为同一个应用设置独立的 dev 和 test 环境时,可以使用它。
🌐 This guide explains how to run more than one local Supabase project on one machine with the experimental supabase stack commands. Use it when you switch between apps, work in several git worktrees, run coding agents in parallel, or want separate dev and test environments for one app.
在本指南中,本地项目是指在你的机器上为一个应用运行的一套 Supabase 服务,这跟 supabase start 给你的效果是一样的。你的应用就是你自己的代码,位于你运行 supabase init 的目录里。
🌐 In this guide, a local project is the set of Supabase services running on your machine for one app, the same thing supabase start gives you. Your app is your own code, in the directory where you ran supabase init.
使用默认端口时,supabase start 在每台机器上只能运行一个本地项目。每个应用生成的 config.toml 都使用相同的端口,所以第二个本地项目会因为端口冲突而失败。supabase stack 命令会为每个本地项目分配自己的端口,并保持其数据独立。
🌐 With the default ports, supabase start runs one local project per machine. Every app's generated config.toml uses the same ports, so a second local project fails with a port conflict. The supabase stack commands assign each local project its own ports and keep its data separate.
本指南涵盖内容:
🌐 This guide covers:
- 《本地项目如何保持隔离》解释了哪些目录、分支和名称会拥有自己的本地项目。如果你使用单体仓库或运行多个代理,先读一下这篇文章吧。
- 开启堆栈命令 和 从 config.toml 移除固定端口 是每个应用的一次性设置。
- 从 在每个应用目录或工作树中启动本地项目 到 停止并销毁本地项目 涵盖日常使用。
[与默认的supabase start](#differences-from-the-default-supabase-start)和[限制](#limitations)列出了切换时发生的变化。
实验性的
supabase stack 命令是实验性的。它们的标志和输出可能会在不同版本之间变化,而且 CLI 的兼容性承诺不涵盖它们。使用这些命令启动的本地项目会保留自己的数据,与任何使用默认 supabase start 启动的本地项目的数据是分开的。
🌐 The supabase stack commands are experimental. Their flags and output can change between releases, and the CLI compatibility promise doesn't cover them. Local projects started with these commands keep their own data, separate from any local project started with the default supabase start.
本地项目如何保持隔离 #
🌐 How local projects stay isolated
每个本地项目都有一个由三部分组成的身份:
🌐 Every local project has an identity made from three parts:
- 项目根目录:从你当前目录开始向上查找,最近包含
supabase/config.toml的目录。如果没有配置文件,项目根目录就是当前目录。 - Git 分支:如果项目根目录在一个 git 仓库中,当前分支是身份的一部分。CLI 会从
.git元数据中读取它,所以不需要安装 git。 - 名称:你可以通过
--stack传入的可选名称。
两个具有不同身份的本地项目永远不会共享容器、进程、端口、数据库数据或存储数据。你会为每个项目得到一个独立的本地项目:
🌐 Two local projects with different identities never share containers, processes, ports, database data, or Storage data. You get a separate local project for each:
- 有自己的
supabase/目录的应用 - Git 工作树,因为每个工作树都是一个不同的目录
- Git 分支,当你在同一个目录下切换到不同分支时
- 你用
--stack传递名字,这样一个应用就可以同时运行dev和test
其他所有东西都共享一个本地项目。除非每个包都有自己的 supabase/ 目录,否则 monorepo 里的包不会有单独的本地项目。位于同一个 supabase/ 目录下的两个包会共享一个本地项目。同样地,在同一个分支的同一个检出目录下工作的两个编码代理也会共享一个本地项目。要想隔离它们,可以使用不同的 git 工作树或者不同的 --stack 名称。
🌐 Everything else shares one local project. Packages in a monorepo get separate local projects only if each one has its own supabase/ directory. Two packages under one supabase/ directory share a local project. So do two coding agents working in the same checkout on the same branch. To isolate them, use separate git worktrees or different --stack names.
同一个项目根目录下的本地项目也会共享其 supabase/ 目录里的项目文件。例如,Studio 会把 SQL 片段保存到 supabase/snippets,所以一个应用的 dev 和 test 本地项目看到的 SQL 片段是一样的。
🌐 Local projects for the same project root also share the project files in its supabase/ directory. For example, Studio saves SQL snippets to supabase/snippets, so the dev and test local projects of one app see the same snippets.
CLI 会从 20000 到 32767 的范围内为每个本地项目分配端口,并在重启时保持稳定。写在 config.toml 的端口会按原样使用,所以先看看 从 config.toml 移除固定端口 吧。
🌐 The CLI assigns ports for each local project from the range 20000 to 32767 and keeps them stable across restarts. Ports written in config.toml are used as written, so see Remove fixed ports from config.toml first.
在你开始之前 #
🌐 Before you begin
你需要:
🌐 You need:
- Supabase CLI v2.119.0 或更高版本。查看 安装并运行 CLI。
- 一个容器引擎或支持的平台。Docker 运行时需要一个正在运行的 Docker 或 Podman 引擎,并且同时是多个本地项目推荐的运行时。原生运行时不需要容器引擎,但只能在 Apple Silicon 的 macOS 和
amd64、arm64的 Linux 上运行。参见 选择运行时。 - 使用
supabase init初始化的应用目录,或者没有supabase/config.toml的目录。如果没有配置文件,本地项目将使用默认设置启动,并且不会创建配置文件。
打开堆栈命令 #
🌐 Turn on the stack commands
supabase stack 命令只有在 [experimental] stack 设置开启时才存在。没有它,supabase stack 命令会以 Unknown subcommand "stack" 失败。
🌐 The supabase stack commands exist only when the [experimental] stack setting is on. Without it, supabase stack commands fail with Unknown subcommand "stack".
-
在你应用目录下的
supabase/config.toml中,在现有的[experimental]表下添加stack = true。如果你的config.toml没有[experimental]表,就添加一个:[experimental]stack = true不要再添加第二个
[experimental]表。重复的表会让config.toml无效。堆栈命令也会因此失效。读取配置的命令会因为failed to load config失败。在没有
config.toml的目录中,可以改为设置环境变量:export SUPABASE_EXPERIMENTAL_STACK=1 -
确认这些命令是否可用:
supabase stack --help帮助从
Manage an experimental, unstable local Supabase stack开始。如果你看到的是一般的Supabase CLI帮助,那说明这个设置没开启。
开启该设置后,supabase start、supabase status 和 supabase stop 会运行堆栈命令。本文其余部分使用这些顶层命令。supabase stack start、supabase stack status 和 supabase stack stop 也做同样的事情。只存在于 supabase stack 下的命令,例如 list 和 destroy,则保持前缀。
🌐 With the setting on, supabase start, supabase status, and supabase stop run the stack commands. The rest of this guide uses those top-level commands. supabase stack start, supabase stack status, and supabase stack stop do the same thing. Commands that only exist under supabase stack, such as list and destroy, keep the prefix.
这个设置还会将 db、migration、test、gen、inspect、pull、storage、seed 和 services 命令以及 functions serve 的本地目标发送到栈中。
🌐 The setting also routes the local targets of the db, migration, test, gen, inspect, pull, storage, seed, and services commands, and functions serve, to the stack.
环境变量优先于配置文件。设置 SUPABASE_EXPERIMENTAL_STACK=0 可以在不编辑文件的情况下,使用默认命令进行一次会话。
🌐 The environment variable takes precedence over the config file. Set SUPABASE_EXPERIMENTAL_STACK=0 to use the default commands for one session without editing the file.
从 config.toml 中移除固定端口 #
🌐 Remove fixed ports from config.toml
supabase init 会把固定端口写入 config.toml。CLI 会把文件中的端口当作准确的请求处理。两个都请求端口 54321 的本地项目不能同时运行。
对于一个新应用,开启设置后运行 init。命令行会写入 [experimental] stack = true,并且不会包含端口密钥:
🌐 For a new app, run init with the setting turned on. The CLI writes [experimental] stack = true and leaves the port keys out:
SUPABASE_EXPERIMENTAL_STACK=1 supabase init对于已经有 config.toml 的应用,删除你想要并行运行的每个应用的端口行:
🌐 For an app that already has a config.toml, remove the port lines from each app that you want to run in parallel:
- 在你的应用目录中打开
supabase/config.toml。 - 删除或注释这些键:
port在[api]、[db]、[db.pooler]、[studio]、[local_smtp]和[analytics]下[db]下的shadow_portinspector_port在[edge_runtime]下smtp_port和pop3_port在[local_smtp]下,vector_port在[analytics]下,如果你设置了它们的话。supabase init会把 SMTP 端口写成注释形式。
- 保存文件并提交,这样团队里的每个人都会有相同的行为。
在应用的第一个 supabase start 之前开启设置并移除端口。本地项目会保留创建时的端口。如果你已经用固定端口启动了一个项目,之后再移除端口会导致 supabase start 因 cannot change on the saved stack 失败。可以用不同的 --stack 名称创建一个新的本地项目,或者运行 supabase stack destroy 然后重新开始。销毁会删除本地项目的数据。
🌐 Remove the ports before the app's first supabase start with the setting on. A local project keeps the ports it was created with. If you already started one with the fixed ports, removing them afterward makes supabase start fail with cannot change on the saved stack. Start a new local project with a different --stack name, or run supabase stack destroy and start again. Destroying deletes the local project's data.
CLI 也会把你通过环境变量设置的端口,比如 SUPABASE_API_PORT 或 SUPABASE_DB_PORT,当作精确请求。如果有其他进程已经在监听该端口,启动就会失败。如果端口属于另一个本地项目,错误信息会标出那个本地项目。
🌐 The CLI also treats ports you set with an environment variable, such as SUPABASE_API_PORT or SUPABASE_DB_PORT, as exact requests. If another process already listens on that port, the start fails. When the port belongs to another local project, the error names that local project.
在每个应用目录或工作树中启动本地项目 #
🌐 Start a local project in each app directory or worktree
以下步骤会从两个应用目录启动两个独立的本地项目。相同步骤也适用于一个仓库的 git 工作树。
🌐 The following steps start two independent local projects from two app directories. The same steps work for git worktrees of one repository.
-
在第一个应用目录中打开终端,然后启动它的本地项目:
supabase start当本地项目准备好时,该命令会输出
Stack is ready.和一个连接摘要。摘要列出了 API、REST、Functions、Studio、MCP、Mailpit 和数据库的 URL,可发布和秘密密钥,每个服务的状态,以及运行时信息。 -
在另一个应用目录或工作树中打开第二个终端,然后运行相同的命令:
supabase start第二个本地项目有自己的端口和数据库。命令执行完后,它们都会继续运行。
-
把每个应用都指向它自己本地项目的项目 URL。下次启动时端口保持不变。要把 URL 和密钥写入 dotenv 文件,请参见 查找本地项目的端点和密钥。
每次运行的第一次启动会更慢。默认情况下,该命令会在返回之前下载或拉取所有启用的服务。之后的启动会重用缓存。如果想把每个下载推迟到该服务第一次请求时再进行,可以传入 --preparation on-demand。
🌐 The first start in each runtime takes longer. By default, the command downloads or pulls every enabled service before it returns. Later starts reuse the cache. To defer each download until the service's first request, pass --preparation on-demand.
Postgres 会立即启动。其他服务在首次请求时启动。默认情况下,它们在空闲 60 秒后再次停止,而 Studio 则在 5 分钟后停止。Functions 不会自动停止。当有运行的服务依赖某个服务时,该服务也会保持运行,例如 Studio 运行时 pgmeta 会保持运行。要在命令返回前启动所有已启用的服务并关闭空闲停止,可以传入 --eager。
🌐 Postgres starts right away. Other services start on their first request. By default, they stop again after 60 seconds idle, and Studio after 5 minutes. Functions doesn't stop on its own. A service also stays up while a running service depends on it, such as pgmeta while Studio runs. To start every enabled service before the command returns and turn off idle stops, pass --eager.
要排除服务,传入 --exclude 并加上一个或多个 rest、auth、realtime、storage、functions、studio、mail、analytics 和 pooler:
🌐 To leave services out, pass --exclude with one or more of rest, auth, realtime, storage, functions, studio, mail, analytics, and pooler:
supabase start --exclude studio,mail数据库不能被排除。Studio 需要 REST API,所以要排除 rest,也要排除 studio。排除 studio 也会移除 MCP 服务器,因为 Studio 提供 /mcp。
🌐 The database can't be excluded. Studio needs the REST API, so to exclude rest, exclude studio too. Excluding studio also removes the MCP server, because Studio serves /mcp.
在正在运行的本地项目上再次运行 supabase start 会保留当前设置并打印 Stack is already running with its current services。要对 config.toml、--exclude 或 --eager 应用更改,需要先停止本地项目再重新启动。不带 --exclude 启动会恢复 config.toml 中启用的服务。
🌐 Running supabase start again on a running local project keeps its current settings and prints Stack is already running with its current services. To apply a change to config.toml, --exclude, or --eager, stop the local project and start it again. Starting without --exclude restores the services enabled in config.toml.
停止和启动不会更改端口或 Postgres 主版本。对于这些情况,可以用不同的 --stack 名称启动一个新的本地项目,或者运行 supabase stack destroy 然后重新开始。
🌐 Stopping and starting doesn't apply changes to ports or to the Postgres major version. For those, start a new local project with a different --stack name, or run supabase stack destroy and start again.
为一个应用运行本地命名项目 #
🌐 Run named local projects for one app
一个应用可以按名字运行多个本地项目。这可以让破坏性测试远离你的开发数据。
🌐 One app can run several local projects by name. This keeps a destructive test run away from your development data.
-
开始一个
dev本地项目:supabase start --stack dev -
在同一目录中启动一个
test本地项目:supabase start --stack test -
把相同的
--stack名称传给supabase status、supabase stop和supabase stack destroy,以后就能针对本地项目了。
没有 --stack 启动的本地项目是应用目录的默认本地项目。命名的本地项目和默认本地项目是相互独立的。
🌐 A local project started without --stack is the app directory's default local project. Named local projects and the default one are independent of each other.
db、migration 和其他数据库命令不接受 --stack。当它们在本地数据库上运行时,会使用当前分支的默认本地项目。supabase db pull、supabase db push 和 supabase db dump 会针对关联项目,除非你传入 --local。要从接受 --db-url 的命令访问指定的本地项目,请传入 supabase status --stack <name> --env 中的 DB_URL。请参见 限制。
查找本地项目的端点和密钥 #
🌐 Find a local project's endpoints and keys
本地项目的端口各不相同,所以不要以为 CLI 文档上的默认值就是标准值。要查看本地项目的 URL、密钥和服务状态,可以在它的应用目录中运行以下命令:
🌐 Ports differ between local projects, so don't assume the defaults from the CLI documentation. To see the URLs, keys, and service states of a local project, run this in its app directory:
supabase status为指定的本地项目传入 --stack <name>。本地数据库密码是 postgres,所以输出中的数据库 URL 可以与 psql 和 --db-url 一起使用。
🌐 Pass --stack <name> for a named local project. The local database password is postgres, so the database URL in the output works with psql and --db-url.
要将连接详情导出为环境变量,传递 --env:
🌐 To export the connection details as environment variables, pass --env:
supabase status --env --output-format text > .env.local该文件包含 API_URL、DB_URL、PUBLISHABLE_KEY、SECRET_KEY、ANON_KEY 和 SERVICE_ROLE_KEY,以及其他可用服务的 URL。为了匹配你的框架期望的变量名,请传递 --override-name:
🌐 The file includes API_URL, DB_URL, PUBLISHABLE_KEY, SECRET_KEY, ANON_KEY, and SERVICE_ROLE_KEY, plus the URLs of the other available services. To match the variable names your framework expects, pass --override-name:
supabase status --env --override-name API_URL=NEXT_PUBLIC_SUPABASE_URL,ANON_KEY=NEXT_PUBLIC_SUPABASE_ANON_KEY对于脚本和代理,请向 supabase start 或 supabase status 请求 JSON:
🌐 For scripts and agents, request JSON from supabase start or supabase status:
supabase start --output-format jsonstart JSON 包含本地项目的 id、它的 runtime、在首次请求时启动的 lazy_services,以及 --env 导出的相同 env 映射。它的 endpoints 对象以服务和端点为键,例如 database.sql,每个条目都有一个 protocol、address、port 和 url。
🌐 The start JSON includes the local project's id, its runtime, lazy_services that start on their first request, and the same env map that --env exports. Its endpoints object is keyed by service and endpoint, such as database.sql, and each entry has a protocol, address, port, and url.
要列出机器上所有应用的每个本地项目,运行:
🌐 To list every local project on the machine, across all apps, run:
supabase stack list该列表显示每个本地项目的名称、项目根目录、分支、运行时和简短 ID。传入 --output-format json 可以获得 --stack-id 接受的完整 ID。
🌐 The list shows each local project's name, project root, branch, runtime, and a short ID. Pass --output-format json to get the full ID that --stack-id accepts.
要从本地项目实时查看日志,运行 supabase stack logs。传入 --service database 来限制输出到一个服务。
🌐 To stream live logs from a local project, run supabase stack logs. Pass --service database to limit the output to one service.
选择一个运行时 #
🌐 Choose a runtime
本地项目可以在 Docker 运行时或原生运行时下运行。Docker 运行时会用 Docker 或 Podman 在容器中运行每个服务。原生运行时则是在你的机器上把每个服务作为进程运行,不依赖容器引擎。它可以在苹果芯片的 macOS 以及 Linux amd64 和 arm64 上运行。两种运行时都会运行相同的服务。
🌐 A local project runs in the Docker runtime or the native runtime. The Docker runtime runs each service in a container, with Docker or Podman. The native runtime runs each service as a process on your machine, with no container engine. It runs on macOS on Apple silicon and on Linux amd64 and arm64. Both runtimes run the same services.
在同一台机器上有多个本地项目时,使用 Docker。容器为每个本地项目提供独立的网络命名空间和文件系统。本地原生项目会共享主机的进程表和文件锁。在没有容器引擎的情况下使用原生运行时,比如代码代理沙箱和 CI 运行器。
🌐 For several local projects on one machine, use Docker. Containers give each local project its own network namespace and file system. Native local projects share the host's process table and file locks. Use the native runtime where no container engine is available, such as coding agent sandboxes and CI runners.
当你没有通过 --runtime 时,新建的本地项目会先使用 Docker(如果 Docker 守护进程有响应),然后是 Podman,再然后是在支持的平台上使用本地运行时。仅安装了 docker 命令是不够的。本地项目的运行时在整个生命周期内是固定的。详情、平台要求以及本地运行时下载的内容,请参见 Docker 和本地运行时。
🌐 When you don't pass --runtime, a new local project uses Docker if the Docker daemon responds, then Podman, then native on supported platforms. Having the docker command installed isn't enough. The runtime is fixed for the life of a local project. For details, platform requirements, and what the native runtime downloads, see Docker and native runtimes.
停止并摧毁本地项目 #
🌐 Stop and destroy local projects
停止本地项目会保留它的数据和端口。销毁它会删除它的数据。
🌐 Stopping a local project keeps its data and ports. Destroying it deletes its data.
要停止应用目录的默认本地项目,在该目录下运行这个命令:
🌐 To stop the app directory's default local project, run this in that directory:
supabase stop要停止一个指定的本地项目,传入它的名称:
🌐 To stop a named local project, pass its name:
supabase stop --stack test要停止机器上的所有本地项目,包括所有应用,传入 --all。如果有本地项目停止失败,命令会以错误退出,并列出失败的本地项目:
🌐 To stop every local project on the machine, across all apps, pass --all. If any local project fails to stop, the command exits with an error that lists the failed local projects:
supabase stack stop --all要永久删除一个本地项目及其数据,运行 supabase stack destroy。该命令会要求确认。在脚本中使用 --yes 可以跳过提示:
🌐 To permanently delete one local project and its data, run supabase stack destroy. The command asks for confirmation. Pass --yes to skip the prompt in scripts:
supabase stack destroy --stack test --yessupabase stack destroy 会删除本地项目的数据库数据并释放端口。没有备份,也无法撤销。而且也没有批量销毁功能,所以每条命令只能移除一个本地项目。
destroy 保存两种数据:
- 存储上传的文件,它们存放在你应用的
supabase/.temp/stack-uploads/<id>/目录下。删除该目录即可移除它们。 - 容器引擎资源,如果 Docker 或 Podman 没在运行。即使没有相关进程在运行,只要你有权限删除主机上的数据库文件,这个命令还是可以移除本地项目的注册。之后它会打印出引擎重新启动后要执行的清理命令。否则操作会失败,会保留本地项目,并提示你启动引擎再重试。你可能无法删除容器写入主机目录的数据库文件。用 Docker 25 或更早版本创建的本地项目就是以这种方式存储数据的。
如果本地项目将其数据库数据存储在 Docker 卷中,其他本地项目也会共享该卷。打印出来的清理命令只会删除该本地项目的数据。不要删除卷本身。
🌐 If the local project stores its database data in a Docker volume, other local projects share that volume. The printed cleanup command deletes only this local project's data from it. Don't delete the volume itself.
与默认 supabase start#
🌐 Differences from the default supabase start
在为使用默认 supabase start 的应用开启该设置时,请记住这些差异:
🌐 Keep these differences in mind when you turn the setting on for an app that used the default supabase start:
- 以这种方式启动的本地项目会保留独立的数据。打开这个设置不会把你用默认
supabase start启动的本地项目的数据库复制过来,也不会停止那个本地项目。 supabase stop --no-backup和supabase stop --project-id不可用。使用supabase stack destroy来删除数据,使用supabase stack stop --all来停止所有本地项目。-o和--output标志不可用。改用--output-format,并用--env替代-o env。functions serve需要一个正在运行的本地项目。先用supabase start启动一个吧。
限制 #
🌐 Limitations
- 有些
config.toml设置不被支持。supabase start会失败,并显示指出该设置的消息。不被支持的设置有:api.tls- Analytics 的 GCP 设置,以及除了 Postgres 之外的 Analytics 后端
- 使用
content_path的自定义认证邮件模板 - 存储分析和存储向量桶
- 一个自定义的
edge_runtime.deno_version - OrioleDB,以及除15和17以外的
db.major_version值
- 数据库命令不接受
--stack。supabase db reset和supabase db diff针对当前分支的默认本地项目运行。supabase db pull、supabase db push和supabase db dump会以关联项目为目标,除非你传入--local。如果要访问一个指定的本地项目,把supabase status --stack <name> --env的DB_URL传给接受--db-url的命令就行。 - 切换分支会切换本地项目。git 分支是身份的一部分。当你在同一个目录下检出另一个分支时,
supabase start会创建或恢复一个不同的本地项目,并有自己的数据。第一个分支的本地项目会一直运行,直到你停止它。 - 移动或重命名应用目录会创建一个新的本地项目,因为身份信息包括项目的根路径。之前本地项目的数据会一直保留,直到你手动删除它。
supabase stack logs只会传输新的日志行,没有历史记录。
Learn more#
- Install the CLI and start your first local project.
Install and run the CLI
- Day-to-day commands for one local project, from migrations to troubleshooting.
Local development workflow
- How the CLI picks a runtime, what the native runtime needs, and where it stores data.
Docker and native runtimes
- Keep configuration and secrets consistent across local, staging, and production.
Managing config and secrets
- Every key in config.toml, including the experimental stack setting.
CLI configuration