堆栈
Stack 用于归组一个项目的应用和服务。其成员共享一个私有网络和一组变量,并可按指定顺序一起部署。
什么是 Stack?
在 API 和 CLI 中,Stack 称为项目(project):由 /api/v1/projects 和 sh0 projects 管理。Stack 为其应用提供:
- 私有网络:每个应用可通过
<app-name>和<app-name>.internal访问,重新部署后主机名不变。 - 共享变量,Stack 中所有应用都会读取。
- 启动顺序,在一次性部署整个 Stack 时使用。
不同 Stack 中的应用无法通过名称互相访问。数据库服务器不属于任何 Stack:任何应用都可以通过 <server-name>.internal 访问它们。
创建 Stack
在控制面板中打开 Stacks,点击 Create Stack,填写 Stack Name(1 到 100 个字符)和可选的 Description。通过 CLI:
$ sh0 projects create my-shop
Project created: my-shop (5aee8eea-9f77-4d10-ac21-2fe4a565e666) Note
sh0 push --project 需要这里输出的 Stack ID,不接受名称。Stack 页面
打开 Stack 后会按类型分组显示其服务,并提供:
- Add App(打开该 Stack 的部署页面)、Stack Settings、Export Snapshot 和 Import Snapshot。
- Internal network & shared settings 面板:显示每个服务的内部主机名、Shared variables、Start order,以及按该顺序部署所有应用的 Deploy the project。
Stack 变量
共享变量会在下一次部署时注入 Stack 中的每个应用。如果应用定义了同名键,以应用的值为准。详情、API 和优先级参见环境变量。
Stack 设置
Stack Settings 可编辑名称、描述、颜色和成员,并提供 Delete Stack。
Warning
Stack 只有为空时才能删除。只要其中还有应用、数据库或服务,删除就会被拒绝,并在消息中列出剩余内容:请先删除它们——真正丢失数据的是这一步。
常见用法
- 每个产品一个 Stack:需要通过名称互相访问的前端、API 和 worker。
- 每个环境一个 Stack:预发布与生产分开,各自拥有共享变量。
- 每个客户一个 Stack:借助 Stack 成员机制,每个客户的应用只对指定人员可见。
通过 API 管理 Stack
路由位于 /api/v1/projects 下。更新使用 PATCH,PUT 返回 405。不带 /v1 的请求不会到达 API,而是返回控制面板页面。以下输出取自一个测试实例:
# Create a stack
$ curl -s -X POST https://panel.example.com/api/v1/projects \
-H "Authorization: Bearer $SH0_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"my-staging","description":"Staging environment"}'
{"data":{"color":null,"created_at":"2026-10-02 02:53:41","description":"Staging environment","id":"b9b14ce5-7aa4-4c0d-85fe-b6c4b3977e8e","member_count":1,"name":"my-staging","owner_id":"50159570-e8ad-46a8-8065-e6c9096edde6"}}
# List stacks (paginated: page, per_page)
$ curl -s https://panel.example.com/api/v1/projects -H "Authorization: Bearer $SH0_TOKEN"
{"data":[{"color":null,"created_at":"2026-10-02 02:53:41","description":"Staging environment","id":"b9b14ce5-7aa4-4c0d-85fe-b6c4b3977e8e","member_count":1,"name":"my-staging","owner_id":"50159570-e8ad-46a8-8065-e6c9096edde6"}, ...],"page":1,"pages":1,"per_page":20,"total":3}
# Get one stack
$ curl -s https://panel.example.com/api/v1/projects/b9b14ce5-7aa4-4c0d-85fe-b6c4b3977e8e -H "Authorization: Bearer $SH0_TOKEN"
{"data":{"color":null,"created_at":"2026-10-02 02:53:41","description":"Staging environment","id":"b9b14ce5-7aa4-4c0d-85fe-b6c4b3977e8e","member_count":1,"name":"my-staging","owner_id":"50159570-e8ad-46a8-8065-e6c9096edde6"}}
# Update a stack
$ curl -s -X PATCH https://panel.example.com/api/v1/projects/b9b14ce5-7aa4-4c0d-85fe-b6c4b3977e8e \
-H "Authorization: Bearer $SH0_TOKEN" -H "Content-Type: application/json" \
-d '{"description":"Staging, rebuilt nightly"}'
{"data":{"color":null,"created_at":"2026-10-02 02:53:41","description":"Staging, rebuilt nightly","id":"b9b14ce5-7aa4-4c0d-85fe-b6c4b3977e8e","member_count":1,"name":"my-staging","owner_id":"50159570-e8ad-46a8-8065-e6c9096edde6"}}
# Delete a stack that still holds an app: refused (HTTP 400)
$ curl -s -X DELETE https://panel.example.com/api/v1/projects/5aee8eea-9f77-4d10-ac21-2fe4a565e666 -H "Authorization: Bearer $SH0_TOKEN"
{"error":{"code":"BAD_REQUEST","key":"project_not_empty","message":"Cannot delete stack: it still has 1 app (my-api). Delete these services first.","params":{"remaining":[{"count":1,"family":"apps","names":"my-api"}]}}}
# Delete an empty stack (HTTP 204, no body)
$ curl -s -X DELETE https://panel.example.com/api/v1/projects/b9b14ce5-7aa4-4c0d-85fe-b6c4b3977e8e -H "Authorization: Bearer $SH0_TOKEN" -o /dev/null -w "%{http_code}\n"
204 Note
全部端点参见 API 参考。