Docs / Core Concepts / Stacks

Stacks

A stack groups the apps and services of one project. Its members share a private network and a set of variables, and can be deployed together in a defined order.

What is a Stack?

In the API and the CLI a stack is called a project: /api/v1/projects and sh0 projects manage stacks. A stack gives its apps:

  • A private network on which each app answers to <app-name> and <app-name>.internal. The hostname does not change when the app is redeployed.
  • Shared variables, read by every app of the stack.
  • A start order, used when the whole stack is deployed at once.

Apps in different stacks do not see each other by name. Database servers are outside stacks: any app reaches them at <server-name>.internal.

Creating a Stack

In the dashboard, open Stacks, click Create Stack, enter a Stack Name (1 to 100 characters) and an optional Description. From the CLI:

Terminal
$ sh0 projects create my-shop
Project created: my-shop (5aee8eea-9f77-4d10-ac21-2fe4a565e666)
Note
sh0 push --project expects the stack ID printed here; the name is not accepted.

Stack Page

Opening a stack shows its services grouped by kind, and:

  • Add App, which opens the Deploy page for this stack; Stack Settings; Export Snapshot and Import Snapshot.
  • The Internal network & shared settings panel: each service's internal hostname, the Shared variables, the Start order, and Deploy the project, which deploys every app in that order.

Stack Variables

A shared variable is injected into every app of the stack at its next deployment. If an app defines the same key, the app's value wins. Details, API and precedence: Environment Variables.

Stack Settings

Stack Settings edits the name, the description, the color and the members, and holds Delete Stack.

Warning
A stack can only be deleted when it is empty. While it still holds apps, databases or services, deletion is refused and the message lists what remains: delete those first, which is where data is actually lost.

Common Patterns

  • One stack per product: the frontend, the API and the workers that must reach each other by name.
  • One stack per environment: staging and production separated, each with its own shared variables.
  • One stack per client: with stack members, each client's apps stay visible only to the people assigned to it.

Managing Stacks via API

The routes live under /api/v1/projects. Updates use PATCH; PUT answers 405. A request without /v1 does not reach the API: it returns the dashboard page. Outputs below were obtained on a test instance:

Terminal
# 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
For every endpoint, see the API Reference.