
Fixing Supabase Migration Conflicts: The 'History Table' Mismatch
Team member changed the schema, and now `supabase db push` fails? Learn how to fix migration history mismatches using `migration repair`.

Team member changed the schema, and now `supabase db push` fails? Learn how to fix migration history mismatches using `migration repair`.
How to deploy without shutting down servers. Differences between Rolling, Canary, and Blue-Green. Deep dive into Database Rollback strategies, Online Schema Changes, AWS CodeDeploy integration, and Feature Toggles.

Solving server waste at dawn and crashes at lunch. Understanding Auto Scaling vs Serverless through 'Taxi Dispatch' and 'Pizza Delivery' analogies. Plus, cost-saving tips using Spot Instances.

Why your server isn't hacked. From 'Packet Filtering' checking ports/IPs to AWS Security Groups. Evolution of Firewalls.

Why would Netflix intentionally shut down its own production servers? Explore the philosophy of Chaos Engineering, the Simian Army, and detailed strategies like GameDays and Automating Chaos to build resilient distributed systems.

I added a nickname column to the profiles table for a new feature.
I generated the migration file locally using supabase db diff and verified it worked.
Confidently, I ran the deploy command:
supabase db push
But the terminal spat out a red error:
Error: migration 20251212000000_add_nickname.sql mismatch
Remote history is different from local history.
"Wait, remote history is different? I just wrote this!" It turned out a teammate (or past me) had touched the server DB directly.
I thought migration conflicts were like Git Merge Conflicts. Can't I just merge the SQL files like code?
No. Databases have State.
In Git, if code conflicts, you rewrite it.
In DB, ALTER TABLE must run in strict order. If order is messed up, data is lost or tables break.
Supabase (Postgres) keeps a strict record of "List of executed migration files" in the supabase_migrations table.
If this History differs from my local file list by even one bit, it refuses deployment for safety.
I understood it when comparing it to a "Blockchain Ledger."
The Server Ledger says [A -> B -> C].
My Local Files say [A -> B -> D].
The server says, "Hey, you skipped C! Or did you tamper with the ledger? Transaction rejected!"
This happens because I ignored C (remote change) and tried to push D, or modified C locally.
Scenario: Server has 2025..._team_change.sql, but my local doesn't.
The standard way is to sync local with remote.
supabase db pull
This brings schema changes from the server to my local DB.
Then run diff again, and only my changes will be generated into a new file.
What if "Does the server change was a mistake? I want to overwrite it with mine!" (e.g., Development environment).
Use migration repair. This asks the server to force-edit the ledger.
# Mark a specific version as 'applied' on server (Skip actual SQL execution)
supabase migration repair --status applied 20251212000000
Conversely, if it says applied but wasn't:
# Delete a specific version from server history (reverted)
supabase migration repair --status reverted 20251212000000
Use this to align local and remote "Version History", then push.
Developing alone? db push is fine. But teams need rules.
migrations files.Recommended Workflow:
supabase start.localhost:54323).supabase db diff -f add_users -> Generate migration file.supabase db push.What if you deploy a migration and it breaks production? You need to undo it.
In Git, git revert works. In DB, you can't just delete the file.
You must run an Inverse Migration. If you added a table, you must DROP it.
Supabase flows are typically "Forward Only". To rollback, you actually create a New Migration that undoes the previous one, and push that forward.
The best rollback is No Rollback. Use the Expand and Contract pattern.
I wanted to rename username to email.
I ran ALTER TABLE users RENAME COLUMN username TO email.
Result: Downtime.
My API servers (running the old code for a few minutes during deploy) tried to SELECT username.
Postgres said "Column does not exist". 500 Error.
email column. Keep username. Write to both triggers.username -> email.email. Deploy.username column.This is Zero Downtime Migration. Database changes must happen SLOWER than Code changes.
"It works on Staging, fails on Prod." This happens when schemas drift apart.
Supabase CLI relies on the linked Project ID.
NEVER link your local CLI to Production directly (supabase link). One wrong db reset and you are fired.
Use GitHub Actions to switch targets dynamically:
- run: supabase db push --project-ref $PROJECT_ID
env:
PROJECT_ID: ${{ github.ref == 'refs/heads/main' && secrets.PROD_ID || secrets.STAGING_ID }}
develop branch -> Staging DB.
main branch -> Production DB.
Keep your local strictly for local development.
Migrations handle "Structure". What about "Data" (e.g., List of Countries, Default Categories)?
seed.sql is great but only runs on db reset (fresh start).
To add data to a live database, use Data Migrations.
-- 20251214_add_countries.sql
INSERT INTO countries (code, name) VALUES ('KR', 'South Korea')
ON CONFLICT (code) DO NOTHING;
Always use ON CONFLICT DO NOTHING or DO UPDATE to make your data migration idempotent. Running it twice shouldn't break anything.
Ever seen a migration hang forever? It's likely a Locking Issue.
ALTER TABLE requires an Access Exclusive Lock. It waits until all other queries on that table finish.
If you have a long-running report query running, your deployment will freeze.
SET lock_timeout = '2s'; -- Fail fast if lock not acquired in 2s
ALTER TABLE orders ADD COLUMN status TEXT;
pg_stat_activity and terminate stuck sessions.Safety first. Don't let a deploy bring down your production DB.
Automating deploy with GitHub Actions prevents conflicts.
name: Deploy Migrations
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
- run: supabase db push
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_DB_PASSWORD }}
SUPABASE_PROJECT_ID: ${{ secrets.SUPABASE_PROJECT_ID }}
Now, as long as code is merged to main, the DB stays up to date.
db diff to generate files and manage them with Git. Use db pull or migration repair to fix the ledger.