> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.docs.xano.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tenants

> Deploy a release to a tenant with xanosdk tenant deploy, which replaces what the tenant serves, and know what it drops, what it keeps, and how to recover when it stops partway.

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.

| Command | What it does |
| - | - |
| `npx xanosdk tenant list` | Every tenant under your workspace. |
| `npx xanosdk tenant get prod` | One tenant's URL and state, including the last release landed there. |
| `npx xanosdk tenant deploy prod v1.4.0` | Replaces what the tenant serves with the release. |
| `npx xanosdk tenant delete prod` | Destroys the tenant and everything it serves. |

## Deploy a release to a tenant

Cut the release first ([Releases](/ship/releases)), then deploy it to each tenant in turn:

```bash theme={null}
npx xanosdk tenant deploy dev v1.4.0
npx xanosdk tenant deploy stage v1.4.0
npx xanosdk tenant deploy prod v1.4.0
```

The release that passed on `stage` is the exact one that reaches `prod`. Off a terminal, as in
CI, add `--yes`.

```
  on your-instance.xano.io · workspace 3
  last landed release "v1.3.2" (deployed 2026-10-02T13:17:36.000Z) — any `deploy --to tenant:` merge since may have changed what it runs
! This lands a release the server holds, so the local pre-flight did not run:
  no plan preview, no prune scope — its tables were checked against the destination, and it alters none there
  what it has instead: a named release that came up and answered, and can be re-landed
? Replace what tenant "prod" serves with release "v1.4.0"? Anything not in the release — an API an earlier landing or a `deploy --to tenant:` merge added — is removed. Static hosting is kept. (y/N) y
→ Landing v1.4.0 on prod
✓ Landed v1.4.0 on prod
  https://your-instance.xano.io/tenant/prod
```

## 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](/build/env-and-secrets).

A name the tenant holds under a different identity is refused before anything is written
(`SDK_IDENTITY_CONFLICT`, exit `2`).

<Warning>
  Do not edit a tenant by hand in the Xano editor. The next tenant deploy replaces anything you
  changed there.
</Warning>

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

```bash theme={null}
npx xanosdk tenant deploy prod v1.3.2 --yes   # recoverWith: put back what prod ran
```

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:

```bash theme={null}
npx xanosdk tenant get prod --json
```

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:

```bash theme={null}
npx xanosdk deploy ./xano/index.ts --to tenant:prod --dry-run   # preview the merge
npx xanosdk deploy ./xano/index.ts --to tenant:prod --seed      # merge, and write the seed rows
```

`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](/ship/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](/govern/rollback-and-recovery) covers it.

## Next

[Rollback and recovery](/govern/rollback-and-recovery): the way back from a release that went
wrong.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.