Skip to main content
A Xano branch holds logic. Tables are shared by every Xano branch in a workspace, so a table change reaches live the moment a release lands, before any --set-live, and deleting the Xano branch it landed on does not undo it. Switching live back to an earlier Xano branch restores logic, never a dropped column or its values. Plan every table change as a change to production data.

What lands, and what is refused

promote compares the release’s tables with your workspace before it lands anything: A refusal exits 2 with SDK_SHARED_SCHEMA_CHANGE and lists every change, additions included, under details.changes. --yes does not waive it.
deploy --to workspace --branch <label> refuses the same way, because a Xano branch stages logic and not schema. A plain deploy --to workspace onto the live Xano branch previews the change instead: it names each column it would drop and asks before it sends anything. On a tenant there are no Xano branches, and a tenant deploy replaces what the tenant serves:
  • A table the release does not carry is dropped with every row. The confirmation names each one with its row count.
  • 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.
--yes skips that confirmation. In CI nobody reads it, so review the change before the tag.

See the change before it lands

promote and tenant deploy run no local plan. The merge path does, and --dry-run sends nothing:
Read droppedColumns in the plan (--json carries it). Every column listed there loses its values when the release lands.

Rename or drop a column across two releases

A column dropped beside a column added is not a rename. Columns have no rename identity, so the old values are destroyed and the new column starts empty. promote calls the pair out under details.pairedColumns. To keep the data, spread the change over two releases: expand, backfill, contract. Say users.name becomes users.display_name:
1

Expand: add the new column (release v1.4.0)

Add display_name beside name. Write both columns, and read display_name, falling back to name while it is empty:
A new column is an addition, so promote v1.4.0 lands it without the flag, and a tenant deploy keeps every existing value.
2

Backfill: copy the values

With v1.4.0 serving, copy name into display_name for every row where display_name is empty, for example with a one-off task. Check the count of rows still empty is zero before you go on.
3

Contract: drop the old column (release v1.5.0)

Remove name from the schema and stop reading it. The drop loses only values you already copied:
On the workspace, each step can be rolled back to the one before it: switching live back to v1.3.2’s Xano branch serves logic that never reads display_name, and the column keeps its values. On a tenant, deploying v1.3.2 again is a replace, so it drops display_name with the values you backfilled; name still holds them, and the backfill can run again. Once v1.5.0 lands, name and its values are gone whatever you roll back to. The same three steps cover a retype (add a column of the new type, backfill it, drop the old one) and a column made required (backfill the nulls first, then require it).

When to pass --allow-shared-schema-changes

Pass it on the run that lands the contract step, after someone has read droppedColumns and checked that nothing still reads the column. Do not put it in a workflow that runs on every tag: the flag is the only point in the release flow where a person confirms that live data changes. CI/CD shows how the pipeline hands that run to a person.
Back up a tenant or workspace in the Xano dashboard before a release that drops or rewrites a column. A rollback restores logic, not data. See Rollback and recovery.

Next

Drift: keep the workspace and your project in step when someone edits in Xano.