Updated May 2026. This guide now reflects v3.0.0 of the migration skill, which includes 65 deterministic steps (up from 38), 12 real-world traps integrated as explicit steps, TanStack detection, auth.identities migration, custom functions/triggers, private bucket support, and comprehensive verification. There is also a claude.ai chat version for users who don't use Claude Code.
I migrated an entire Lovable Cloud project to my own Supabase in one session. Database schema, 216 rows of data, auth users with original passwords, 37 storage files, 12 edge functions, and 240 frontend files. Everything.
For a long time, we in the community were finding workarounds - edge function exports, REST API calls, manual schema recreation. They worked, but they had gaps. Passwords couldn't be exported. Schemas had to be rebuilt by hand. Storage was a pain. The Lovable MCP changes all of that - this is genuinely a life-changer for migrations.
Here is how.
Ready to migrate?
Paste this into Claude Code and replace the placeholders with your info:
https://github.com/CarolMonroe22/lovable-cloud-to-supabase-migration
I want to migrate my Lovable Cloud project to my own Supabase.
Here is what I have:
- Lovable project ID: [YOUR_PROJECT_ID]
- Lovable workspace ID: [YOUR_WORKSPACE_ID]
- Supabase organization: [YOUR_ORG_NAME or "I'll pick one"]
I need: schema, data, auth users with passwords, storage,
edge functions, and frontend code. Walk me through it.Claude Code reads the skill from the repo and handles most of the migration for you. It will ask for your approval at key points, and you only need to do 2 things manually in the Lovable dashboard (connect Supabase and connect GitHub to the new project).
If you prefer to install the skill first:
npx skills add CarolMonroe22/lovable-cloud-to-supabase-migrationOnce installed, the skill activates automatically whenever you ask about migrating from Lovable Cloud. It asks which level you want:
- Guided - explains each step, confirms before proceeding (recommended for first time)
- Standard - moves faster, confirms key decisions only
- Express - asks for source and destination, runs everything
Not using Claude Code? There is a claude.ai chat version that runs directly from the browser. It covers the backend migration (schema, data, auth, storage, edge functions) but delegates the frontend code to Claude Code on the web or your local terminal. More about it here.
Need to connect the MCPs first? Follow the MCP setup guide.
The rest of this post explains what's happening under the hood so you understand the process, no matter your technical level.
Do You Actually Need to Migrate?
Before we get into the how, let's talk about the why. Lovable Cloud is a solid managed database. It works well for a lot of projects and there's no reason to migrate if it's meeting your needs.
Consider migrating when:
- You need direct Supabase dashboard access for monitoring and management
- You need custom email templates (Cloud sends from no-reply@auth.lovable.cloud)
- You need staging and production environments with separate databases
- You want full control of your infrastructure
If Cloud is working for you, there's no rush. But if you need more - now you know you can get there without losing anything.
What This Migration Involves
Here's the full picture. The migration has 9 phases and 65 steps. Most of it is automated through the MCPs - you just need to click twice in the Lovable dashboard.
| Phase | What happens | Key steps |
|---|---|---|
| 1. Scan source | Read schema, data, auth users + identities, functions, triggers, sequences, indexes, storage, config.toml, tech stack | 19 |
| 2. Create destination | New Supabase project via MCP (defaults to us-west-1) | 5 |
| 3. Apply schema | Extensions, enums, tables, sequences with last_value, custom functions, triggers, indexes, RLS | 9 |
| 4. Auth users | Migrate with original password hashes AND identities | 3 |
| 5. Insert data | All tables in FK order, rewrite URLs in all text/jsonb columns | 5 |
| 6. Storage | Public and private buckets, files via edge function, URL rewrite | 6 |
| 7. Edge functions | Deploy with correct verify_jwt per function, report required secrets | 4 |
| 8. Frontend code | Detect tech stack, push via GitHub, rebuild in Lovable | 9 |
| 9. Verify | Compare 12 categories between source and destination, scan for old URL leaks | 5 |
1. Scan source
Read schema, data, auth users + identities, functions, triggers, sequences, indexes, storage, config.toml, tech stack
19
2. Create destination
New Supabase project via MCP (defaults to us-west-1)
5
3. Apply schema
Extensions, enums, tables, sequences with last_value, custom functions, triggers, indexes, RLS
9
4. Auth users
Migrate with original password hashes AND identities
3
5. Insert data
All tables in FK order, rewrite URLs in all text/jsonb columns
5
6. Storage
Public and private buckets, files via edge function, URL rewrite
6
7. Edge functions
Deploy with correct verify_jwt per function, report required secrets
4
8. Frontend code
Detect tech stack, push via GitHub, rebuild in Lovable
9
9. Verify
Compare 12 categories between source and destination, scan for old URL leaks
5
Now let's walk through each one.
What You Need
| Tool | What for | Cost |
|---|---|---|
| Claude Code | Runs the MCPs that make this possible | Pro $20/mo or Max |
| Lovable MCP | Reads from your Lovable Cloud database | Free (connected via /mcp) |
| Supabase MCP | Creates and configures your new Supabase | Free (auto-available in Claude Code) |
| GitHub CLI | Pushes frontend code | Free |
Claude Code
Runs the MCPs that make this possible
Pro $20/mo or Max
Lovable MCP
Reads from your Lovable Cloud database
Free (connected via /mcp)
Supabase MCP
Creates and configures your new Supabase
Free (auto-available in Claude Code)
GitHub CLI
Pushes frontend code
Free
If you have Lovable Cloud, you're already paying for Lovable. The only potential extra cost is Claude Code ($20/mo) and a Supabase project ($0-10/mo depending on your plan).
Setup docs:
The Migration, Phase by Phase
Phase 1: Scan Your Lovable Cloud Database
This is where the Lovable MCP shines. Connect it via /mcp in Claude Code, then you can run SQL directly against your Cloud database. The skill scans everything it needs in one pass:
| What | Query | Why |
|---|---|---|
| Tables | information_schema.tables | Know what you're migrating |
| Columns and types | information_schema.columns | Recreate exact schema |
| Foreign keys | information_schema.table_constraints | Get the relationships right |
| Custom enums | pg_type + pg_enum | Custom types need to be created first |
| RLS policies | pg_policies | Security rules must match |
| Custom functions | pg_get_functiondef | handle_new_user and other critical functions |
| Triggers | information_schema.triggers | Wire functions to tables |
| Sequences | pg_sequences (with last_value) | Prevent duplicate IDs after migration |
| Custom indexes | pg_indexes | Keep queries fast |
| All data | SELECT * FROM each_table | Your actual data |
| Auth users | SELECT id, email, encrypted_password FROM auth.users | Users WITH passwords |
| Auth identities | SELECT * FROM auth.identities | Session recovery and password reset |
| Storage buckets | storage.buckets (with public flag) | Know which are public vs private |
| Storage URLs | Scan ALL text/jsonb columns for storage references | URLs hide in unexpected places |
| Tech stack | package.json from source repo | Detect classic (Vite) vs modern (TanStack) |
| Edge function config | supabase/config.toml | Read verify_jwt per function |
Tables
information_schema.tables
Know what you're migrating
Columns and types
information_schema.columns
Recreate exact schema
Foreign keys
information_schema.table_constraints
Get the relationships right
Custom enums
pg_type + pg_enum
Custom types need to be created first
RLS policies
pg_policies
Security rules must match
Custom functions
pg_get_functiondef
handle_new_user and other critical functions
Triggers
information_schema.triggers
Wire functions to tables
Sequences
pg_sequences (with last_value)
Prevent duplicate IDs after migration
Custom indexes
pg_indexes
Keep queries fast
All data
SELECT * FROM each_table
Your actual data
Auth users
SELECT id, email, encrypted_password FROM auth.users
Users WITH passwords
Auth identities
SELECT * FROM auth.identities
Session recovery and password reset
Storage buckets
storage.buckets (with public flag)
Know which are public vs private
Storage URLs
Scan ALL text/jsonb columns for storage references
URLs hide in unexpected places
Tech stack
package.json from source repo
Detect classic (Vite) vs modern (TanStack)
Edge function config
supabase/config.toml
Read verify_jwt per function
That auth.users query is the one that changes everything. You can read the actual bcrypt password hashes - which means users keep their exact same login credentials after migration. No password reset. No "please create a new account." That was previously considered impossible.
With the full scan done, the skill knows exactly what it's working with. Time to create the destination.
Phase 2: Create Your Supabase Project
Via the Supabase MCP:
- Pick your organization
- Confirm the cost
- Create the project (defaults to us-west-1 to avoid capacity issues)
- Wait for it to be active (takes a couple of minutes)
The MCP handles all of this. You'll get a project URL and anon key automatically. Now the destination is ready - let's build the schema.
Phase 3: Apply the Schema
This phase follows a strict order. Getting it wrong means FK violations and missing dependencies.
- Enable pg_net extension - needed for storage migration in Phase 6
- Create custom enums (CREATE TYPE)
- Create tables in FK order (parent tables before children)
- Create sequences with the correct last_value from the source (prevents duplicate IDs)
- Create custom functions - including handle_new_user (auto-creates profiles on signup)
- Create triggers - wire the functions to their tables
- Create custom indexes - keep queries performing as expected
- Enable RLS and apply all policies
If handle_new_user is missing, new user signups succeed but no profile gets created. The app crashes when it tries to load profile data. This was one of the 12 traps we discovered during testing.
Schema is ready. Before we can insert data, we need the users in place.
Phase 4: Migrate Auth Users
This goes BEFORE your data. Why? Because your profiles table probably has a foreign key to auth.users. If you insert profiles first, the FK breaks.
INSERT INTO auth.users (
id, instance_id, email, encrypted_password,
email_confirmed_at, raw_user_meta_data,
created_at, updated_at, role, aud
) VALUES (
'{original_id}',
'00000000-0000-0000-0000-000000000000',
'{email}',
'{original_bcrypt_hash}', -- the EXACT hash, not a new password
now(),
'{metadata}'::jsonb,
'{original_created_at}',
now(),
'authenticated',
'authenticated'
);Copy the bcrypt hash exactly as-is. Never generate temporary passwords.
You also need to migrate auth.identities alongside users. Without it, login works but password recovery fails, session refresh tokens break, and OAuth users can't re-link their accounts. Each user typically has one identity row with provider = 'email'.
Optional: If you want existing sessions to remain valid (no forced re-login), copy the JWT secret from the source dashboard to the destination. Settings > API > JWT Secret. There's no API for this - it has to be done manually.
Users are in place. Now the data can go in safely.
Phase 5: Insert Your Data
Order matters here too:
- Catalog/reference tables first - products, categories, types (things without user FKs)
- User-owned tables - profiles, brands, campaigns
- Junction/relation tables - campaign_products, campaign_experiences, etc.
Since auth.users already exist from Phase 4, the profiles FK works naturally. No need to drop constraints or do workarounds.
URL rewriting: After inserting data, the skill scans ALL text and jsonb columns for references to the old Supabase project ref and rewrites them. Not just columns named *_url or *_image - URLs hide in JSONB metadata fields, config columns, and text arrays. Missing these means broken images even though the files were migrated correctly.
Data is in. Next up: the actual files.
Phase 6: Migrate Storage
- Create buckets in the new Supabase, matching the original visibility (public or private)
- Deploy a temporary edge function that downloads files and uploads to new storage
- For private buckets, generate signed URLs from the source database instead of using public URLs
- Rewrite URLs in the database
The skill handles both public and private buckets. In v2, private buckets were silently skipped - the download returned 400 and files appeared migrated but were actually missing.
Files are moved. Now let's bring the backend logic over.
Phase 7: Deploy Edge Functions
For each function:
- Read the source code from the GitHub repo
- Read the verify_jwt setting from config.toml (saved in Phase 1)
- Deploy with the correct verify_jwt per function
This matters more than you'd think. Webhooks (Stripe, custom integrations) need verify_jwt = false. If deployed with verify_jwt = true, they reject every incoming request with 401. The reverse is also a problem - user-facing functions with verify_jwt = false become anonymously callable.
After deployment, the skill reports which secrets each function needs. Things like LOVABLE_API_KEY, OPENAI_API_KEY, STRIPE_SECRET_KEY. You set these manually in the Supabase dashboard (Settings > Edge Functions > Secrets). Functions will fail at runtime until the secrets are configured.
Backend is fully migrated. The last big piece is the frontend.
Phase 8: Move the Frontend Code
This is the part that took the most trial and error. Here's what I learned:
- Remix doesn't work - it copies the project BUT inherits Lovable Cloud. Can't disconnect it.
- GitHub integration is one-way... mostly - Lovable pushes to GitHub, but won't pull from it. Except it actually does pick up commits pushed from outside.
TanStack detection. Lovable changed its default project template to TanStack Start on May 6, 2026. The skill reads package.json from the source to detect whether it's classic (Vite) or modern (TanStack) and creates the destination project with the correct tech_stack. If the wrong stack is used, the git push succeeds but Lovable can't build it.
The working approach:
- Detect tech stack from source package.json
- Create a new empty Lovable project with the correct tech_stack
- Connect your Supabase (manual - dashboard click)
- Connect GitHub (manual - dashboard click)
- Clone the original project's GitHub repo locally
- Copy all files to the new repo and push
- Lovable picks up the commit
- Tell the agent to rebuild
Those 2 dashboard clicks are the only manual steps in the entire migration. Once the rebuild finishes, there's one thing left to do.
Phase 9: Verify
This phase is critical. Don't skip it.
The skill runs a comprehensive audit comparing 12 categories between source and destination:
Category Source Destination Status
tables N N OK
enums N N OK
rls_policies N N OK
functions_custom N N OK or MISSING
triggers N N OK or MISSING
sequences N N OK or MISSING
indexes_custom N N OK or MISSING
auth_users N N OK
auth_identities N N OK or MISSING
storage_buckets N N OK
storage_objects N N OK or PARTIAL
old_ref_leaks 0 0 OKIf anything shows MISSING or PARTIAL, the skill tells you exactly which phase and step to re-run. Then test login with original credentials to confirm the full auth flow works.
12 Traps (So You Don't Have To)
During testing, I found 12 things that cause silent failures during migration. Each one is now an explicit step in the skill, but here's the summary so you know what to watch for:
| Trap | What happens if missed | Severity |
|---|---|---|
| Wrong tech_stack on create_project | Lovable can't build after git push | Critical |
| us-east-1 capacity outage | Project creation fails | Degradation |
| pg_net not enabled | Storage migration fails | Degradation |
| Private buckets assumed public | Files silently missing | Silent bug |
| verify_jwt not read per function | Webhooks 401 or security hole | Silent bug |
| Edge function secrets not listed | Functions fail at runtime | Silent bug |
| URLs in JSONB not rewritten | Broken images | Silent bug |
| Custom functions not migrated | New signups break (no profile) | Critical |
| Sequences reset to 1 | Duplicate IDs | Critical |
| Custom indexes not recreated | Slow queries | Degradation |
| auth.identities not migrated | Password recovery fails | Critical |
| Verification only counts tables | Missing pieces go unnoticed | Silent bug |
Wrong tech_stack on create_project
Lovable can't build after git push
Critical
us-east-1 capacity outage
Project creation fails
Degradation
pg_net not enabled
Storage migration fails
Degradation
Private buckets assumed public
Files silently missing
Silent bug
verify_jwt not read per function
Webhooks 401 or security hole
Silent bug
Edge function secrets not listed
Functions fail at runtime
Silent bug
URLs in JSONB not rewritten
Broken images
Silent bug
Custom functions not migrated
New signups break (no profile)
Critical
Sequences reset to 1
Duplicate IDs
Critical
Custom indexes not recreated
Slow queries
Degradation
auth.identities not migrated
Password recovery fails
Critical
Verification only counts tables
Missing pieces go unnoticed
Silent bug
Full details with fixes in trampas.md on GitHub.
The Numbers
For my test migration, here's what the final audit looked like:
| Component | Migrated |
|---|---|
| Tables | 13 |
| Custom enums | 5 |
| RLS policies | 41 |
| Custom functions | 4 (including handle_new_user) |
| Triggers | 3 |
| Sequences | 1 (order_number_seq) |
| Custom indexes | 6 |
| Data rows | 216 |
| Auth users | 4 (with original passwords) |
| Auth identities | 4 |
| Storage files | 37 |
| Edge functions | 12 (with correct verify_jwt) |
| Frontend files | 240 |
| Old URLs remaining | 0 |
| Manual steps | 2 |
Tables
13
Custom enums
5
RLS policies
41
Custom functions
4 (including handle_new_user)
Triggers
3
Sequences
1 (order_number_seq)
Custom indexes
6
Data rows
216
Auth users
4 (with original passwords)
Auth identities
4
Storage files
37
Edge functions
12 (with correct verify_jwt)
Frontend files
240
Old URLs remaining
0
Manual steps
2
Everything else was automated via the MCPs. Zero data loss. Zero broken URLs. Users can log in with their original credentials.
After Migration: What to Watch For
A few things to keep in mind once you're migrated:
- If your project uses Google OAuth, you'll need to update the redirect URL in Google Console to point to your new Supabase project. Otherwise users will get a 403 when trying to log in with Google.
- Storage files from the old project will stop working if the original Lovable Cloud project is deleted. Make sure all URLs are rewritten before that happens.
- Edge function secrets need to be configured in the new project's dashboard. The skill gives you the full list per function after Phase 7.