Database migrations with the Supabase CLI for AI-built apps
How to bring an AI-built Supabase database under migrations with the CLI: capture the current schema, make changes as files, test locally and push safely.
Leer en español →
A migration is a SQL file that describes one change to your database, stored in supabase/migrations in your repository and applied in order. With the Supabase CLI you capture the schema your AI tool built (supabase db pull), write each new change as a file (supabase migration new), test it on a local copy (supabase db reset) and apply it to production (supabase db push). The point is that every change is reviewed, repeatable and in Git, instead of typed into the dashboard and forgotten.
This guide is for apps that started in Lovable, Bolt, Cursor or similar and now need a safer way to change the database. It covers the one-time setup, the everyday loop, and the mistakes that most often break db push.
Why bother with migrations at all?
Because the most expensive bugs in AI-built apps are database changes nobody can retrace: a column renamed in the dashboard that the code still uses, a table created in production but never in the test project, a policy someone "fixed" by hand. Migrations solve that by making the database a set of files:
- You can see exactly what changed, when and why, in Git history.
- You can rebuild the same database for a staging environment or a new developer in one command.
- Mistakes are caught on a local copy before they touch real data.
Many builders already write SQL files into a supabase/migrations folder when they change your schema. Check your repository; if the folder exists, you are halfway there.
What do I need before starting?
- The Supabase CLI installed on your machine.
- Docker running. The CLI starts a local Postgres in Docker for
db reset,db diffanddb pull. - Your project's code in Git, ideally connected to GitHub so you can work on a branch.
- The project ref (the ID in your project's URL) and your database password.
How do I bring an existing project under migrations?
Do this once, on a branch.
supabase login
supabase init # creates the supabase/ folder if missing
supabase link --project-ref abcdefghijklmnop
supabase db pull # writes the current remote schema as a migration
supabase db pull creates a migration file in supabase/migrations that captures what is in production right now. If the folder already had migration files from your builder, check supabase migration list first. It shows which migrations are applied locally and remotely, and where the two disagree.
When history and reality disagree (for example, the builder's files don't match what's actually in production), the CLI's supabase migration repair command can mark a migration as applied or reverted. It only edits the history record and runs no SQL, so use it to correct bookkeeping, never to "apply" a change.
Then confirm the files rebuild your database from scratch:
supabase start # local stack
supabase db reset # applies every migration, then supabase/seed.sql
If the reset fails, fix the files until it succeeds. A migration history that can't rebuild the database is the problem you're trying to remove.
What does the everyday loop look like?
For each change, follow the same steps. The official database migrations guide describes the same flow.
1. Create a migration file
supabase migration new add_due_date_to_tasks
This creates supabase/migrations/<timestamp>_add_due_date_to_tasks.sql. Write the change in it:
alter table public.tasks add column due_date date;
create index tasks_due_date_idx on public.tasks (due_date);
2. Or capture a change you made locally
If you prefer clicking in the local Studio, make the change on your local database and generate the file afterwards:
supabase db diff -f add_due_date_to_tasks
Make these changes on the local database only. Changes made directly on the remote project bypass the migration history and are a common reason db push later fails.
3. Test it
supabase db reset
Then run the app against the local stack and click through the parts that use the table.
4. Commit and review
Commit the migration with the code that uses it, open a pull request and read the SQL. This is the moment to catch a drop column that shouldn't be there.
5. Apply it to production
supabase db push --dry-run # shows what would be applied
supabase db push
Take a backup first if the change removes or rewrites data; our guide to Supabase backups shows how. If you work in a team, only one person should push at a time, because migrations are applied in timestamp order.
What belongs in a migration besides tables?
Everything that defines how the database behaves:
- RLS and policies.
alter table ... enable row level security;and everycreate policybelong in the same migration as the table. See our RLS policy examples. Grants. Since May 30, 2026, new Supabase projects don't expose new tables to the Data API automatically, and Supabase says existing projects get the same default on October 30, 2026. A new table needs explicit grants, so write them in the migration that creates it:
grant select, insert, update, delete on public.tasks to authenticated;Without this, the app gets
permission denied for table taskseven with correct policies.- Functions, triggers and indexes.
- Storage buckets and their policies, if your app depends on them.
Test data goes in supabase/seed.sql, not in migrations, so it never reaches production by accident.
How do I keep the AI tool from undoing this?
If you keep using an AI builder, tell it the rules. In Cursor or Claude Code, put them in your project rules: "never change the database through the dashboard or MCP; create a new file with supabase migration new and include RLS and grants". Our guide to writing Cursor rules shows how. With Lovable or Bolt, review the SQL the tool proposes before approving it, and keep production changes going through the CLI.
Quick answers
Can I edit a migration that's already applied? Don't. Write a new migration that changes it. Editing an applied file means production and the history no longer match.
How do I undo a migration? Write a new migration that reverses it, for example dropping the column you added. There is no automatic "down" step to rely on.
Do I need a separate Supabase project for staging? It's strongly recommended once you have real users. You push the same migrations to staging first, then to production.
Does db push move my data? No. It changes the structure. Data stays where it is, which is why destructive changes need a backup first.
Want a hand setting it up?
Getting an AI-built project's history straight is fiddly the first time. A Supabase security review checks the schema, policies and grants, and a Dev Subscription gives you a developer to set up the workflow with you. You can also contact us with questions.

