Zero-downtime schema changes
Learn how to safely apply and roll back schema changes in Xata using the CLI.
You'll use the xata roll
commands to manage migrations, and an example with changing column types.
To better understand the use case and our novel approach to the challenge of schema changes, see the schema changes core concept page for more details.
xata roll
is embedding pgroll behind the scenes and additional details around schema changes can be found in the pgroll documentation.
1. Prerequisites
- A Xata account (sign up here)
- The Xata CLI installed:
curl -fsSL https://xata.io/install.sh | bash
- Access to your Xata PostgreSQL branch
2. Create a Xata project and branch
In the Console, create a new project and then click the Create main branch button to create the PostgreSQL instance.
Since this is a production setup, we recommend using at least 1 replica. It's a good idea to start with a similar instance size as you are running in your current PostgreSQL provider. You will be able to adjust it later.
3. Configure the Xata CLI
Authenticate the CLI by running:
xata auth login
Initialize the project by running:
xata init
You would typically run xata init
command in your application source directory, or in any directory/repository where you'd like to keep the configuration. It will create a .xata
folder with several configuration files.
Follow the instructions from the prompt to select the project and branch that you created at the previous step.
4. Prepare a migration directory
Initialize migration tracking on your current Xata branch:
xata roll init
This command sets up migration history for your branch.
5. Create initial tables
First, let's create a migration file for our initial schema:
cat > .xata/migrations/001_initial_schema.yaml << 'EOL'
operations:
- create_table:
name: products
columns:
- name: id
type: serial
- name: name
type: text
- name: price
type: numeric(7,2)
constraints:
- name: products_pk
type: primary_key
columns:
- id
- create_table:
name: orders
columns:
- name: id
type: serial
- name: created_at
type: timestamptz
default: now()
constraints:
- name: orders_pk
type: primary_key
columns:
- id
- create_table:
name: order_items
columns:
- name: order_id
type: int
- name: product_id
type: int
- name: qty
type: int
constraints:
- name: order_items_pk
type: primary_key
columns:
- order_id
- product_id
- name: order_items_order_id_fk
type: foreign_key
columns:
- order_id
references:
table: orders
columns:
- id
- name: order_items_product_id_fk
type: foreign_key
columns:
- product_id
references:
table: products
columns:
- id
EOL
Apply the migration:
xata roll migrate --complete true
6. Insert sample data
Now let's insert some sample data using psql:
psql `xata branch url`
Then run the following SQL to insert sample data:
INSERT INTO products(name,price) SELECT LEFT(md5(i::text),8),(random()*90+10)::numeric(7,2) FROM generate_series(1,10)i;
WITH o AS (INSERT INTO orders DEFAULT VALUES RETURNING id) INSERT INTO order_items(order_id,product_id,qty) SELECT o.id,pid,(1+floor(random()*3))::int FROM o,(SELECT id pid FROM products ORDER BY random() LIMIT 5)p;
7. View and pull changes
You can view the changes in your branch details page in the Xata Console. The changes will be tracked automatically.
To pull the latest migrations locally, run:
xata roll pull
This will write the complete schema history as .yaml files to the .xata/migrations
directory.
8. Create a new schema change
Let's create a migration that:
- Changes the
qty
column in theorder_items
table from integer to float - In the
up
function (current schema → new schema):- Converts any quantity less than 1 to 1.0
- Converts all other values from integer to float
- In the
down
function (new schema → current schema):- Uses the column value directly for rollback
Create the migration file with this command:
cat > .xata/migrations/002_alter_order_items.yaml << 'EOL'
operations:
- alter_column:
table: order_items
column: qty
type: float
up: |
CASE
WHEN qty < 1 THEN 1.0
ELSE qty::float
END
down: qty
EOL
9. Start the migration
Start the migration using:
xata roll migrate
This command starts the migration but leaves it uncompleted, allowing you to roll back if needed.
10. Roll back the migration
When a migration has started, it is serving up both version of the schema simultaneously. You can instantly revert any schema changes during an active migraiton by rolling back the changes. To test rolling back, run:
xata roll rollback
11. Check migration status
Let's start the migration again and check its status:
xata roll migrate
xata roll status
The migration should be In progress
and you can view all statuses available here.
12. Complete the migration
To finalize the migration, run:
xata roll complete
In a production scenario, you would only complete the migration when the application code is up to date with the database changes.
13. (Optional) Add to CI/CD with GitHub Actions
To automate schema changes in your CI/CD pipeline, you can use our GitHub Actions workflow. This workflow creates a dedicated Xata branch for each pull request, allowing you to test database changes in isolation.
For details about setting up the workflow and available configuration options, see our GitHub Actions PR workflow documentation.
14. Summary
- Use
xata roll migrate
to apply migrations - Use
xata roll status
to check migration state - Use
xata roll rollback
to undo migrations if needed - Use
xata roll complete
to finalize migrations when your application is ready - For more details on up/down migrations, see the pgroll documentation.
- See the CLI reference for all available
xata roll
commands.