Validate the migration
Run end-to-end checks on your destination Supabase project after migration: row counts, a sample API read, an auth check, storage, and manual follow-ups.
Migration finishes when the last row is inserted, but you are not done until you have confirmed the destination behaves like the source. Run these checks against your own Supabase project before you point production traffic at it.
Row counts
Compare counts table by table. A mismatch means an insert batch was skipped or rejected — usually a foreign key violation or a duplicate key.
SELECT relname AS table, n_live_tup AS rows
FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY relname;
n_live_tup is an estimate refreshed by autovacuum. For an exact number on a suspect table, run SELECT count(*) FROM public.your_table; on both source and destination and diff them.
A sample API read
The REST layer relies on RLS and the correct roles. Hit PostgREST with your destination anon key:
curl "https://<project-ref>.supabase.co/rest/v1/your_table?select=*&limit=1" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer $ANON_KEY"
An empty array with HTTP 200 usually means RLS is blocking the read, not that data is missing. Confirm with a service-role query. A 42P01 maps to relation does not exist — the table is in a schema PostgREST cannot see.
An auth check
Auth users are inserted directly into auth.users with their bcrypt hashes preserved, so a real login is the only honest test:
curl "https://<project-ref>.supabase.co/auth/v1/token?grant_type=password" \
-H "apikey: $ANON_KEY" -H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"<known-password>"}'
A returned JWT confirms the hash and identity rows carried over. 400 invalid_grant means the credential or the identity row is wrong.
A storage read
List objects and download one file:
curl "https://<project-ref>.supabase.co/storage/v1/object/public/<bucket>/<path>" \
-H "apikey: $ANON_KEY" --output check.bin
Confirm the byte size matches the source object.
Manual follow-ups
Some things cannot be copied and are listed in your report:
- Rotate secrets — API keys and DB passwords stored on the old project should be regenerated, not reused.
- Re-add OAuth providers — Google, GitHub and similar client IDs/secrets are configured in the dashboard, not in the database.
- Redeploy Edge Functions — function code and secrets do not travel with the schema.
If any check fails, see troubleshooting for the specific error and fix.