Skip to content

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 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.

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.

Terminal window
npx tori.host env set API_TOKEN=secret
npx tori.host env list
npx tori.host env unset API_TOKEN

See tori env for flags. The same keys can be set in the console on the app → env tab.

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.

Terminal window
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.

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:

Terminal window
npx tori.host

For an image you brought, pass the same digest:

Terminal window
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”.

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.

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.

Terminal window
curl -sS -H "Authorization: Bearer $TORI_TOKEN" \
https://api.tori.host/api/apps/<slug>/env
Terminal window
curl -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>/env

Shapes and status codes: API reference (listEnvVars, setEnvVar, removeEnvVar). Do not copy tables from there into this page.

Download this page as Markdown.