scaledbox.toml reference
A product describes itself in scaledbox.toml at the root of its repository: its services, the
resources every environment gets, and what differs per environment. Unknown settings are refused.
Sizes are Kubernetes quantities (256Mi, 1Gi), CPU in cores or millicores (100m),
durations Go's (60s, 15m).
[app]
The product.
| Setting | Type | Description |
|---|---|---|
name |
string | The product's name, as approved: lower-case letters, digits and single dashes, at most 30 characters |
mode |
string | "static": a static platform, with no server and no cluster: every service is static on Cloudflare (provider = "cloudflare"), and scaledbox deploy applies them there itself, from CI, with a Cloudflare token of the platform's own (CLOUDFLARE_API_TOKEN: Workers Scripts: Edit on the account; Zone: Read, DNS: Read and Workers Routes: Edit on its zones; CLOUDFLARE_ACCOUNT_ID, default: its first domain's zone's account). Nothing else is declared (no [postgres], [bucket], [release], [previews], [status], [tenants], [ai] or [notify]). Default: a platform with a server (saas or single, as its cluster file says), which applies every deploy, its static services on Cloudflare included, the CLI never reaching Cloudflare |
[services.<service>]
A service: a box app on scaledbox's runtime (JavaScript modules your build made, Workers-shaped handlers, and its static assets), answering at its domains if it has any, else a worker; or a static site served by Cloudflare (provider) (names: lower-case letters, digits and single dashes).
| Setting | Type | Description |
|---|---|---|
dir |
string | Where its build put it, relative to scaledbox.toml (default: the service's name): its box.toml (main: its entry module; bindings, fetched hosts, routes, cron, limits) and its JavaScript modules (.js, .mjs, each with its source map .map); scaledbox deploy sends them |
assets |
string | Its static files' directory, relative to scaledbox.toml (none: no assets): served at its names beside its fetch (box.toml's [assets] says which paths its fetch answers) |
domains |
array of strings | Its public names, over HTTPS: the product's own domains (e.g. example.com, staging.example.com), each answering once its records hold (scaledbox domains) |
health |
string | An HTTP path its fetch answers 2xx at when it works: a box of each new release is asked it before the release goes live, and it is its names' uptime check |
replicas |
integer | A fixed number of boxes, all kept awake, 1 to 50 (default 1): the same as min_replicas and max_replicas both set to it |
min_replicas |
integer | Boxes kept awake, 0 to 50 (default 1): never asleep, their top level and timers running; 0: it sleeps when idle and wakes on its next request, cron tick or queue delivery, as previews do |
max_replicas |
integer | The most boxes, 1 to 50 (default: min_replicas): a request goes to the awake box with the fewest in flight, and spills over to the next once each has 20, asleep again when idle. Reserved on the fleet at max_replicas, so a range within it admits nothing more |
memory |
string | Each box's memory and its cap, e.g. 512Mi (default 256Mi, 32Mi to 2Gi); reserved on the fleet, for each of its max_replicas |
env |
table of strings | Plain variables, each a string in env (env.LOG_LEVEL); secrets are set with scaledbox secrets set, never here, and are strings in env too |
provider |
string | Where it is served (default: the product's clusters). "cloudflare": a static service (its assets; its box.toml, if it has one, without main) served by Cloudflare as Workers static assets at its domains (Workers Custom Domains, in zones of the platform's account), one Worker <app>-<service>-<environment> each. scaledbox deploy uploads its files to the platform's API as a box's; the platform's server keeps it at Cloudflare with the platform's token (CLOUDFLARE_WORKERS_TOKEN and CLOUDFLARE_ACCOUNT_ID in the platform's .env, which sync gives it): planned and applied once the release is live, what is no longer declared removed, and put back if it is changed there by hand. Free on Workers' free plan; each file at most 25 MiB, at most 20,000 files. Not per environment, not in previews; no health, replicas, memory or env |
script |
string | A static platform's service only (mode = "static"): one small Worker script for paths that need a key, a JavaScript module (.js or .mjs, at most 1 MiB, without imports), relative to scaledbox.toml and outside its assets. It runs first on script_paths only (Workers' run_worker_first), reaching the files through its ASSETS binding (env.ASSETS.fetch) and the Worker's secrets (set at Cloudflare, kept by every deploy) through env; every other path is served as a file (free: only requests running a script count against the free plan's 100,000 a day). Deployed with the files by the same deploy |
script_paths |
array of strings | The paths script runs first on, as Workers' run_worker_first rules: each starts with / (* matches any characters, e.g. /v2/*), or !/ to exclude one; at least one not excluded. Required with script |
[postgres.<database>]
A database and its role in every environment, on the product's own Postgres instance: the services' database bindings (and kv, queues, workflows, objects) reach main; others are reached by tools (names: lower-case letters and digits, at most 10). Removing it never drops it.
| Setting | Type | Description |
|---|---|---|
size |
string | Its size, reserved (the product's Postgres instance's disk is sized to hold every environment's): beyond it, it turns read-only (default 1Gi) |
[bucket]
Object storage in every environment: its own prefix of the product's bucket at scaledbox's S3 gateway, with its own keys; the services' bucket bindings reach it (R2's API); presigned URLs work.
| Setting | Type | Description |
|---|---|---|
public |
boolean | Anyone may read its objects (public-read); writing needs its keys |
domain |
string | A public bucket's own name, e.g. files.example.com: GET https://<domain>/<key> reads its object <key>, through Cloudflare's CDN; proven as the product's other domains (scaledbox domains shows its records) |
size |
string | Its size, reserved: beyond it, nothing more can be written (default 5Gi) |
max_object |
string | Its largest object, e.g. 50M (default 25M, at most 100M: 100,000,000 bytes, Cloudflare's request body): a larger upload, multipart part or multipart total is refused (400 EntityTooLarge) |
[release]
Before each release goes live, a service's release handler runs in a box of the new release (e.g. the database's migrations), within its duration: if it fails, nothing changes.
| Setting | Type | Description |
|---|---|---|
service |
string | The service whose release handler runs: its default export's release(event, env), event { environment, release, first, preview } (first: the environment's first release, e.g. a preview's sample data then) |
[env.<environment>]
What differs in one environment (e.g. prod, staging).
[env.<environment>.services.<service>]
Settings of [services.<service>] for this environment only: each one given here replaces the shared one (env variables are merged by name).
[previews]
Previews of pull requests (scaledbox deploy --env pr-<number> --preview): one service at pr-<number>.<domain>, every service asleep when idle, with databases of their own (empty, or copies of another environment's: branch_from), deleted with the environment or after a week without a deploy.
| Setting | Type | Description |
|---|---|---|
service |
string | The service that answers, at pr-<number>.<domain>; the others run without a name |
domain |
string | One of the product's own domains, a zone's apex (previews are one level below it, as Cloudflare's free certificate covers); its DNS points *.<domain>, or each preview's name, at the product's target |
branch_from |
string | An environment (e.g. staging) each preview's databases are copies of, as they are when the preview is first deployed: each [postgres.<name>] that environment has, its files cloned in a fraction of a second on a data node's ZFS pool (its sessions end for about 0.1 s; its apps' pools reconnect). A database it lacks, or one on an instance not yet on a pool, starts empty. Without it, previews' databases start empty (the release handler's first release fills them: event.first) |
[tenants]
Where the product's tenants' apps answer.
| Setting | Type | Description |
|---|---|---|
domain |
string | One of the product's own domains or a name below one, or a name one label below a zone of the platform's (e.g. apps.example.com, whose wildcard the platform publishes itself): its tenants' apps answer at <subdomain>.<domain> once its wildcard record holds (scaledbox domains) |
[status]
The product's public status page, served by its cluster: its environments' names, whether each answers and its uptime over 24 hours, 7 and 30 days.
| Setting | Type | Description |
|---|---|---|
domain |
string | Its name, one of the product's own domains (e.g. status.example.com), proven like the others (scaledbox domains) |
environment |
string | The environment whose deploys claim and route it (default prod) |
[notify]
Where its environments' deploys are announced: each one's start, success, failure and rollback (previews' aren't: their pull requests' checks are).
| Setting | Type | Description |
|---|---|---|
chat |
string | The name of the environment's secret holding a Slack incoming webhook's URL (a Discord webhook's and an ntfy topic's work too): its deploys are posted there. Set it in each environment announced (scaledbox secrets set --env prod DEPLOYS_SLACK, its value the webhook's URL): a deploy of an environment without it is refused |
webhook |
boolean | Also send them to the product's webhook (PUT /v1/webhook) as deploy.started, deploy.succeeded, deploy.failed and deploy.rolled_back events, signed and retried as its others (Standard Webhooks) |
[ai]
The product's AI gateway: its tenants' apps call LLMs through their ai binding (env.AI) with the product's own provider keys, and hand a long answer's stream to their client while they sleep; always on, its memory reserved on the fleet (in one environment, default prod).
| Setting | Type | Description |
|---|---|---|
domain |
string | Its name, one level below the product's tenant domain (e.g. ai.example.com when [tenants] domain is example.com), under its wildcard record and certificate: the URLs a box hands its clients a stream at |
memory |
string | Memory reserved for it and its limit, e.g. 256Mi (default 128Mi, 64Mi to 4Gi), held on the fleet as a service's: it holds about 70 KiB a stream under way beside its ~12 MiB |
environment |
string | The environment whose deploys run it and whose secrets hold its keys (default prod) |
[ai.providers.<provider>]
Its providers by name (lower-case letters, digits and dashes): tenants name a model <provider>/<model>, e.g. anthropic/claude-sonnet-4-5.
| Setting | Type | Description |
|---|---|---|
api |
string | The API it speaks: openai-completions (OpenAI's Chat Completions, and OpenAI-compatible providers), openai-responses (OpenAI's Responses), anthropic-messages (Anthropic's Messages) or google-generative-ai (Google's Gemini API) |
base_url |
string | Its API's base (https), e.g. an OpenAI-compatible provider's, OpenRouter's or Groq's (default: OpenAI's, Anthropic's or Google's own) |
key |
string | The name of the environment's secret holding the product's API key for it (scaledbox secrets set --env prod OPENAI_KEY): the gateway calls it with that key; tenants never see it |