Supabase does not have a built-in rollback command. To reverse a migration, write a new migration that undoes the changes (drop the table, remove the column, or revert the policy), then apply it with supabase db push. For local development, use supabase db reset to reapply all migrations from scratch. If a migration was applied but is broken, use supabase migration repair to mark it as reverted in the migration history.
Rolling Back Migrations in Supabase
Unlike ORMs like Prisma that have built-in rollback commands, Supabase follows a forward-only migration strategy. When you need to undo a migration, you write a new migration that explicitly reverses the changes. This tutorial explains the three rollback strategies: writing reverse migrations for production, using supabase db reset for local development, and using supabase migration repair to fix broken migration history. You will learn when to use each approach and how to avoid data loss.
Prerequisites
- Supabase CLI installed and linked to your project
- Familiarity with SQL (CREATE TABLE, ALTER TABLE, DROP statements)
- Existing migrations in your supabase/migrations/ directory
- A backup of your database if rolling back in production
Step-by-step guide
Identify the migration to roll back
Identify the migration to roll back
List your migrations to find the one you need to reverse. Each migration file in supabase/migrations/ has a timestamp prefix and a descriptive name. Check the supabase_migrations.schema_migrations table to see which migrations have been applied to your remote database. This tells you whether the migration has already been pushed to production or only exists locally.
1# List local migration files2ls supabase/migrations/34# Check which migrations have been applied remotely5supabase migration listExpected result: You can see the list of migration files and which ones have been applied to the remote database.
Write a reverse migration for production rollback
Write a reverse migration for production rollback
Create a new migration file that undoes the changes made by the original migration. If the original created a table, the reverse drops it. If it added a column, the reverse removes it. If it created an RLS policy, the reverse drops that policy. Be careful with destructive operations — dropping a table permanently deletes all data in it. Consider backing up the data before running the reverse migration.
1# Create the reverse migration2supabase migration new revert_add_products_table34# Then edit the generated file:5# supabase/migrations/20260327120000_revert_add_products_table.sqlExpected result: A new empty migration file is created in supabase/migrations/ ready for your reverse SQL.
Write the reverse SQL statements
Write the reverse SQL statements
Open the generated migration file and write SQL that reverses each change from the original migration. Work in reverse order — if the original migration created a table and then added an RLS policy, first drop the policy and then drop the table. If the original added a column with data, decide whether you need to preserve that data (copy it to another column or table first) before removing the column.
1-- supabase/migrations/20260327120000_revert_add_products_table.sql23-- Step 1: Drop RLS policies first (they reference the table)4DROP POLICY IF EXISTS "Users can view products" ON products;5DROP POLICY IF EXISTS "Users can insert products" ON products;6DROP POLICY IF EXISTS "Users can update own products" ON products;7DROP POLICY IF EXISTS "Users can delete own products" ON products;89-- Step 2: Drop indexes10DROP INDEX IF EXISTS idx_products_user_id;1112-- Step 3: Drop the table13DROP TABLE IF EXISTS products;1415-- For column removal instead of table drop:16-- ALTER TABLE products DROP COLUMN IF EXISTS category_id;Expected result: The reverse migration file contains SQL statements that undo all changes from the original migration.
Apply the reverse migration
Apply the reverse migration
For local development, run supabase migration up to apply the new reverse migration. For production, push the migration to your remote database with supabase db push. The CLI will apply any pending migrations in order, including your reverse migration. Verify the changes by checking the table structure in the Dashboard or running a query in the SQL Editor.
1# Apply locally2supabase migration up34# Push to production5supabase db push67# Verify the rollback8supabase db diff # Should show no unexpected changesExpected result: The reverse migration is applied, and the original changes are undone.
Use supabase migration repair for broken migrations
Use supabase migration repair for broken migrations
If a migration was recorded in the history table but failed partially, or if you manually reverted changes in the Dashboard and need to update the migration history, use supabase migration repair. This command marks a specific migration as either applied or reverted without actually running any SQL. Use it to fix inconsistencies between your migration files and the database state.
1# Mark a migration as reverted (not applied)2supabase migration repair --status reverted 2026032710000034# Mark a migration as applied (already applied manually)5supabase migration repair --status applied 2026032710000067# Verify the migration status8supabase migration listExpected result: The migration history is updated to reflect the correct state of each migration.
Use supabase db reset for local development rollback
Use supabase db reset for local development rollback
During local development, the fastest way to roll back is supabase db reset. This drops the entire local database, reapplies all migrations from scratch, and runs the seed file. Simply delete or rename the migration file you want to undo before running reset. This approach is only for local development — never use it on a production database.
1# Remove the migration you want to undo2rm supabase/migrations/20260327100000_add_products_table.sql34# Reset the local database (reapplies remaining migrations + seed)5supabase db reset67# Or keep the file but reset to test the full sequence8supabase db resetExpected result: The local database is rebuilt from scratch without the removed migration.
Complete working example
1-- Reverse migration: Undo the add_products_table migration2-- Original migration created: products table, RLS policies, indexes3-- This migration removes all of those in reverse order45-- =============================================6-- Step 1: Remove RLS policies7-- (Must be dropped before the table they reference)8-- =============================================9DROP POLICY IF EXISTS "Anyone can view products"10 ON public.products;1112DROP POLICY IF EXISTS "Authenticated users can insert products"13 ON public.products;1415DROP POLICY IF EXISTS "Users can update their own products"16 ON public.products;1718DROP POLICY IF EXISTS "Users can delete their own products"19 ON public.products;2021-- =============================================22-- Step 2: Remove indexes23-- =============================================24DROP INDEX IF EXISTS public.idx_products_user_id;25DROP INDEX IF EXISTS public.idx_products_created_at;2627-- =============================================28-- Step 3: Remove the trigger (if any)29-- =============================================30DROP TRIGGER IF EXISTS set_products_updated_at31 ON public.products;3233-- =============================================34-- Step 4: Drop the table35-- (CASCADE would also drop dependent objects,36-- but explicit drops above are safer)37-- =============================================38DROP TABLE IF EXISTS public.products;Common mistakes when rollling Back a Migration in Supabase
Why it's a problem: Deleting a migration file from supabase/migrations/ without updating the remote migration history
How to avoid: If the migration was already pushed to production, use supabase migration repair --status reverted <version> to update the migration history before removing the file.
Why it's a problem: Dropping a table in a reverse migration without backing up its data first
How to avoid: Before running the reverse migration in production, export the table data using pg_dump -t tablename or the SQL Editor CSV export. Only then apply the destructive migration.
Why it's a problem: Running supabase db reset on a production database instead of writing a reverse migration
How to avoid: Never use db reset on production. It drops and recreates the entire database. Always write a targeted reverse migration for production rollbacks.
Why it's a problem: Forgetting to drop RLS policies before dropping the table they reference
How to avoid: Drop policies, then indexes, then triggers, then the table itself. Using IF EXISTS on each statement prevents errors if some objects were already removed.
Best practices
- Always write reverse migrations as new forward migrations rather than editing existing migration files
- Use IF EXISTS in all DROP statements to make reverse migrations idempotent
- Name reverse migrations with a revert_ prefix for clarity
- Back up production data before applying any destructive reverse migration
- Test reverse migrations locally with supabase db reset before pushing to production
- Keep migration files in version control so the full history of changes and reversals is tracked
- Use supabase migration repair only when the migration history is inconsistent with the actual database state
- Drop dependent objects (policies, indexes, triggers) before the objects they reference (tables, columns)
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I applied a Supabase migration that added a products table with RLS policies and indexes, but I need to revert it. Show me how to write a reverse migration in SQL that safely drops everything in the correct order, and explain how to apply it with the Supabase CLI.
Write a SQL migration that safely reverts a table creation including RLS policies, indexes, and triggers. Use IF EXISTS to make it idempotent. Then show the supabase CLI commands to create, test locally, and push the reverse migration.
Frequently asked questions
Does Supabase have a built-in rollback command?
No. Supabase uses forward-only migrations. To undo a migration, write a new migration that reverses the changes. This is a deliberate design choice that keeps the migration history clear and auditable.
What happens if I delete a migration file that was already pushed to production?
The remote database will still have the changes applied, but supabase migration list will show a mismatch. Use supabase migration repair --status reverted to update the history, then write a new reverse migration to undo the schema changes.
Can I use supabase db reset on my production database?
No. supabase db reset drops and recreates the entire database. It is designed for local development only. For production, always write targeted reverse migrations.
How do I roll back a migration that added a column with data?
First export or back up the column data if you need it. Then create a reverse migration with ALTER TABLE your_table DROP COLUMN IF EXISTS column_name. Apply it with supabase db push.
What is supabase migration repair used for?
It updates the migration history table without running any SQL. Use it when the recorded migration state does not match the actual database state — for example, if you manually reverted changes via the Dashboard SQL Editor.
Can I revert multiple migrations at once?
Yes, write a single reverse migration that undoes multiple original migrations. Order the DROP statements carefully — reverse the order of the original creations. Or write separate reverse migrations for each one and apply them in sequence.
Can RapidDev help with complex database migration rollbacks?
Yes. RapidDev can audit your migration history, write safe reverse migrations, test them against a staging environment, and apply them to production with proper backup procedures in place.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation