Deploying from CI

CI deploys with a deploy token: scoped to one project (optionally one environment), expiring, revocable, audited. It may deploy and read that project, nothing else: no secrets, no tokens, no other projects. Any CI works the same way; GitHub Actions is the example below.

1. Create a deploy token

scaledbox tokens create ci-prod --env prod          # --days 90 by default; without --env: every environment

The token (sbx_…) is shown once. Store it as a CI secret, e.g. SCALEDBOX_DEPLOY_TOKEN in the repository's GitHub Actions secrets. Rotate it before it expires: create a new one, update the secret, then revoke the old one (scaledbox tokens list, scaledbox tokens revoke <id>).

2. Build your services' modules

Each service's dir (in scaledbox.toml) must hold its box.toml and its built JavaScript modules (ES modules; TypeScript and JSX are bundled by you), each module's source map beside it, and its assets directory its static files. Any bundler does: esbuild, for example.

      - uses: actions/setup-node@<pinned>
        with:
          node-version: 24
      - run: npm ci
      - run: npx esbuild src/index.ts --bundle --format=esm --platform=neutral --sourcemap --outfile=dist/web/index.js
      - run: cp box.toml dist/web/

Nothing is built on scaledbox and no image is pushed: the deploy sends the files.

3. Deploy in the workflow

The CLI reads SCALEDBOX_TOKEN instead of a sign-in, so a workflow that installs it (curl -fsSL https://scaledbox.dev/install.sh | sh) deploys with the token. It sends scaledbox.toml and each service's files by their SHA-256, uploads only those scaledbox lacks, and waits for the release:

      - name: Deploy to scaledbox
        env:
          SCALEDBOX_TOKEN: ${{ secrets.SCALEDBOX_DEPLOY_TOKEN }}
        run: |
          curl -fsSL https://scaledbox.dev/install.sh | sh
          ~/.local/bin/scaledbox deploy --env prod

A failed release leaves the previous one running; the step fails with the reason (e.g. the release handler's error, or a health path that didn't answer).

Without the CLI, the API takes the same three steps: POST /v1/projects/<project>/environments/<env>/deploys with scaledbox.toml and each service's files' hashes (it answers the missing ones, in buckets), POST .../deploys/<id>/files with each bucket (multipart, a part per file named by its hash), then POST .../deploys/<id>/finish, which answers the release to poll at GET .../releases/<number> until it is live or failed.

A pull request's workflow deploys its preview the same way (--env pr-<number> --preview) and deletes it when the pull request closes (scaledbox environments delete --env pr-<number>).