Environment variables
Environment variables are the process environment a service is started with. Names must match ^[A-Z][A-Z0-9_]*$ — DATABASE_URL, PORT, S3_BUCKET. The app is read from ./tori.json.
Managed Postgres
Section titled “Managed Postgres”Managed Postgres is not generally available. tori db create provisions a database in the app’s zone. When tori db status reports ready, the platform writes DATABASE_URL onto this environment (masked in tori env list). It is not printed by tori db. The running revision still uses the snapshot it started with — deploy again for the process to see the URL. Query-parameter edits of that URL (for example pgbouncer=true) are kept while the database exists. A URL to another host is left alone. When the database is deleted and the zone confirms it is gone, a URL for that pooler role is removed.
Each plan caps concurrent sessions and database size (limits.database on GET /api/account). Statements longer than 60 seconds are cancelled. Crossing the size cap makes tori db status report that writes are read-only; existing reads keep working. DATABASE_URL is transaction-mode PgBouncer, so recovery is one transaction: BEGIN; SET TRANSACTION READ WRITE; TRUNCATE the_table; COMMIT;. A plain DELETE does not shrink the database. Writes resume once usage is under 90 % of the cap.
Dumps and restore
Section titled “Dumps and restore”Shared-tier databases take a nightly custom-format dump (pg_dump -Fc) into the cell object store. Seven successful daily dumps are kept. This is not PITR: the recovery point is the last successful dump (up to about 25 hours), and recovery time is provisioning a restore database plus pg_restore. Dedicated-cluster point-in-time recovery is out of scope.
tori db dumps lists dumps for the owner. tori db restore --from <dump-id> restores into a separate Postgres database. It never overwrites the live app database and never rewrites DATABASE_URL. Only one restore may be ready at a time. tori db restore-status prints the restore Postgres database and role names; the password is not printed (same as tori db create). Connecting from a laptop is not in this slice — the copy exists so an operator can inspect it in the zone, or so a throwaway service can be pointed at it later.
This dump and restore path is not generally available yet.
See tori db.
npx tori.host env set API_TOKEN=secretnpx tori.host env listnpx tori.host env unset API_TOKENSee tori env for flags. The same keys can be set in the console on the app → env tab.
Release-phase migrations
Section titled “Release-phase migrations”A service deploy can run a command in a Job after the image is in the zone and before the new revision takes traffic. Set "release" in tori.json or pass --release. A Prisma / Alembic / Drizzle tree without that setting only prints a hint; it does not run a Job. Set "release": false to silence the hint.
npx tori.host deploy --image registry.example.com/acme/api@sha256:<64 hex> --release "npx prisma migrate deploy"The Job is bounded at 10 minutes (activeDeadlineSeconds, including image pull). That bound is not adjustable. A --release string is split on whitespace with no quoting (npx prisma migrate deploy); use a JSON array in tori.json for arguments that contain spaces. A building deploy is still closed by the control plane after 20 minutes from insert, so a wait-for-previous-Job plus a full 10-minute command can lose to that deadline — cancel and redeploy promptly rather than after a long migration. A successful Job then switches the serving revision. A failing or timed-out Job does not: the previous revision keeps serving. That is not an automatic rollback of database changes — a migration that already committed stays committed. Schema changes must stay readable by the revision that is still live until you deploy a matching app.
A console or API rollback of a deploy that had a release command runs that same command as a Job before traffic moves. If the Job fails, the rollback does not switch the revision. Forward-only migrations that the older image cannot apply will fail the rollback; keep schema changes backward-compatible, or deploy a matching image instead of rolling back.
Cancel of a building deploy does not SIGKILL a still-running release Job: the command is allowed to finish, then the Job is collected. The cancelled revision is not switched live. A following deploy waits for that Job to finish before starting its own — two release commands of one app do not run at once. The wait still counts against the 20-minute building deadline.
Retrying the same deploy reuses the Job named after that deploy id; a new deploy is a new Job. Prisma, Alembic and Drizzle apply heads idempotently. The cell-agent image in the zone must include this field; an older agent ignores release and would pin the revision without running the command.
See tori deploy.
Next deploy, not now
Section titled “Next deploy, not now”A change applies on the next deploy, not immediately. Each deploy takes a snapshot of the environment, so a revision that is already running keeps the one it started with. After env set or env unset, deploy again from the directory that holds tori.json.
For a source-built service:
npx tori.hostFor an image you brought, pass the same digest:
npx tori.host deploy --image registry.example.com/acme/api@sha256:<64 hex>That is enough: the new snapshot differs, so a new revision is created. --restart is a different switch — it forces a revision when image and environment are already live — and it is only valid together with --image. Do not run tori deploy --restart on its own.
The CLI says so after every write. Treat a successful env set without a following deploy as “stored, not yet running”.
Values never come back
Section titled “Values never come back”The API returns values masked and there is no endpoint that does otherwise. env list can show that a variable is set, and not what it is set to:
API_TOKEN sec••••••••The listing goes to stdout and the commentary to stderr, so piping gets the rows without the paragraph. Rotate a leaked secret by setting a new value and deploying; you cannot read the old one back.
Static sites
Section titled “Static sites”A static site is files served from object storage: nothing runs, so nothing reads a variable. The CLI refuses env set and env unset on a static site. The HTTP API and the console env tab have no such guard: they store the row, and every static deploy still snapshots no environment, so the values are inert. env list still works and says so.
Values for a static build belong in the build that produced dist/, not here.
Use these against a service. A PUT on a static app still stores the row (201 on create, 200 on update); that write is never read.
curl -sS -H "Authorization: Bearer $TORI_TOKEN" \ https://api.tori.host/api/apps/<slug>/envcurl -sS -X PUT -H "Authorization: Bearer $TORI_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"key":"API_TOKEN","value":"secret"}' \ https://api.tori.host/api/apps/<slug>/envShapes and status codes: API reference (listEnvVars, setEnvVar, removeEnvVar). Do not copy tables from there into this page.
Download this page as Markdown.