Skip to main content
RapidDev - Software Development Agency
supabase-tutorial

How to Roll Back a Migration in Supabase

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.

What you'll learn

  • Why Supabase uses forward-only migrations instead of automatic rollbacks
  • How to write a reverse migration to undo schema changes
  • How to use supabase migration repair to fix the migration history
  • How to use supabase db reset for local rollback during development
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced8 min read15-20 minSupabase CLI v1.100+, PostgreSQL 15+March 2026RapidDev Engineering Team
TL;DR

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

1

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.

typescript
1# List local migration files
2ls supabase/migrations/
3
4# Check which migrations have been applied remotely
5supabase migration list

Expected result: You can see the list of migration files and which ones have been applied to the remote database.

2

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.

typescript
1# Create the reverse migration
2supabase migration new revert_add_products_table
3
4# Then edit the generated file:
5# supabase/migrations/20260327120000_revert_add_products_table.sql

Expected result: A new empty migration file is created in supabase/migrations/ ready for your reverse SQL.

3

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.

typescript
1-- supabase/migrations/20260327120000_revert_add_products_table.sql
2
3-- 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;
8
9-- Step 2: Drop indexes
10DROP INDEX IF EXISTS idx_products_user_id;
11
12-- Step 3: Drop the table
13DROP TABLE IF EXISTS products;
14
15-- 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.

4

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.

typescript
1# Apply locally
2supabase migration up
3
4# Push to production
5supabase db push
6
7# Verify the rollback
8supabase db diff # Should show no unexpected changes

Expected result: The reverse migration is applied, and the original changes are undone.

5

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.

typescript
1# Mark a migration as reverted (not applied)
2supabase migration repair --status reverted 20260327100000
3
4# Mark a migration as applied (already applied manually)
5supabase migration repair --status applied 20260327100000
6
7# Verify the migration status
8supabase migration list

Expected result: The migration history is updated to reflect the correct state of each migration.

6

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.

typescript
1# Remove the migration you want to undo
2rm supabase/migrations/20260327100000_add_products_table.sql
3
4# Reset the local database (reapplies remaining migrations + seed)
5supabase db reset
6
7# Or keep the file but reset to test the full sequence
8supabase db reset

Expected result: The local database is rebuilt from scratch without the removed migration.

Complete working example

supabase/migrations/20260327120000_revert_add_products_table.sql
1-- Reverse migration: Undo the add_products_table migration
2-- Original migration created: products table, RLS policies, indexes
3-- This migration removes all of those in reverse order
4
5-- =============================================
6-- Step 1: Remove RLS policies
7-- (Must be dropped before the table they reference)
8-- =============================================
9DROP POLICY IF EXISTS "Anyone can view products"
10 ON public.products;
11
12DROP POLICY IF EXISTS "Authenticated users can insert products"
13 ON public.products;
14
15DROP POLICY IF EXISTS "Users can update their own products"
16 ON public.products;
17
18DROP POLICY IF EXISTS "Users can delete their own products"
19 ON public.products;
20
21-- =============================================
22-- Step 2: Remove indexes
23-- =============================================
24DROP INDEX IF EXISTS public.idx_products_user_id;
25DROP INDEX IF EXISTS public.idx_products_created_at;
26
27-- =============================================
28-- Step 3: Remove the trigger (if any)
29-- =============================================
30DROP TRIGGER IF EXISTS set_products_updated_at
31 ON public.products;
32
33-- =============================================
34-- Step 4: Drop the table
35-- (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.

ChatGPT Prompt

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.

Supabase Prompt

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.