Skip to main content
A tenant is a deployment under your workspace with its own URL and its own database. Teams often keep three, dev, stage and prod, and ship the same release to each in turn. Tenants are created in Xano; the CLI lists them, deploys releases to them, and deletes them.

Deploy a release to a tenant

Cut the release first (Releases), then deploy it to each tenant in turn:
The release that passed on stage is the exact one that reaches prod. Off a terminal, as in CI, add --yes.

A tenant deploy replaces

Unlike promote, which adds a Xano branch beside live, a tenant deploy replaces: afterwards the tenant serves exactly the release. Before it asks, it reads the tenant and names what that costs.
  • Objects the release does not carry are removed, including an API that an earlier landing or a deploy --to tenant:<name> merge added. Cut a release that carries everything the tenant should serve.
  • Tables the release does not carry are dropped with every row. The confirmation names each one with its row count.
  • Tables it carries keep their rows, unless the release was cut with --seed. A seeded release replaces the rows of each table it carries rows for, and the confirmation names both counts (table items: 300 rows replaced by 3 seed rows).
  • Changed columns are rewritten for good. A dropped column loses its values. A retyped column’s values are converted ("1.5" becomes 2), and a column made required has its nulls rewritten to the type’s empty value. Read the warnings before you confirm.
  • Static hosting is kept. The tenant’s frontend keeps serving.
  • Env is add only. A name the tenant already holds keeps its value. See Env and secrets.
A name the tenant holds under a different identity is refused before anything is written (SDK_IDENTITY_CONFLICT, exit 2).
Do not edit a tenant by hand in the Xano editor. The next tenant deploy replaces anything you changed there.

When a tenant deploy stops partway

A tenant deploy can stop partway when the tenant’s rows break something the release adds: a unique index over rows that share a value, a required field that rows leave empty, or a stored value a retype cannot convert. The tenant may then serve part of the new release and part of the old one. The run fails with completed: "no" and partlyReplaced: true, names the constraint it hit, and prints recoverWith: the tenant deploy of the release the tenant ran before.
That restores the previous release’s logic and definitions. Values a conversion already rewrote stay rewritten. Fix the rows, then deploy v1.4.0 again. If the outcome is unknown (exit 9), compare the tenant’s deployedAt with what it was before the run. tenant get reports it:
A deployedAt that moved means the release landed. One that did not move means it did not land, but a deploy that stopped partway leaves it unmoved too, so deploy the previous release again to be sure.

Projects with files

A release carries no file bytes, so a project whose tables hold files, or whose MCP servers declare hostedFile() icons, cannot be cut as a release (SDK_RELEASE_FILES_NOT_CARRIED). Deploy it to the tenant directly instead, which ships its files:
deploy --to tenant:<name> merges rather than replaces, and leaves no release behind. A later tenant deploy of a release replaces what it added. Merge path covers its flags.

Going back

Going back on a tenant is deploying the previous release: npx xanosdk tenant deploy prod v1.3.2. It is a replace like any other, so a table the newer release added is dropped with its rows, and values a conversion rewrote are not converted back. Rollback and recovery covers it.

Next

Rollback and recovery: the way back from a release that went wrong.