← All playbooks
Databases migration

PlanetScale → Supabase

Move a PlanetScale Vitess MySQL database into Supabase Postgres. Dump through pscale if live queries stop at 100,000 rows. Load with pgloader into the public schema. Turn on RLS. Do not use the transaction pooler on port 6543 for the load.

Typical timeline10–22 business days36–80 hours active work
Statusneeds review
Source profilePlanetScale Vitess dump docs reviewed 2026-10-29
Destination profileSupabase Postgres import and platform migration docs reviewed 2026-10-29
Documentation date
Review window
Evidence3 sources
This route needs review.Use it as a planning baseline, then verify the dated sources and test with representative data before production.
Before you migrate

Should you make this move?

Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.

Current platform

PlanetScale

Reasons to stay
  • Branching, deploy requests, and Metal performance make Vitess MySQL or managed Postgres appealing for production apps
  • A mature data model and query ecosystem supports serious production workloads
Reasons to leave
  • Engine choice, Vitess limits, and paid-plan economics change how you dump, branch, and cut over
  • Schema design, migration, tuning, and operations require specialist attention
New platform

Supabase

What gets better
  • PostgreSQL, authentication, storage, realtime, and edge functions offer an open backend stack
  • Managed infrastructure removes a large amount of operational work
What gets worse
  • Production scaling and service integration still demand database and infrastructure judgment
  • Platform-specific services can create architectural lock-in
Best of the move

Supabase: PostgreSQL, authentication, storage, realtime, and edge functions offer an open backend stack. This removes a major source-side concern: Engine choice, Vitess limits, and paid-plan economics change how you dump, branch, and cut over.

Worst of the move

What you lose: Branching, deploy requests, and Metal performance make Vitess MySQL or managed Postgres appealing for production apps. What you inherit: Production scaling and service integration still demand database and infrastructure judgment.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome12 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 3, Manual rebuild: 6, Not transferred: 3.
Clean0
Partial3
Manual6
Lost3
Mapping route8 of 9 fields have a destination path

This timeline assumes

  • The source is PlanetScale Vitess (MySQL). A PlanetScale Postgres source is a different dump/restore path.
  • You can pause writes. pgloader and dump/restore are snapshots, not live Vitess replicas.
  • Supabase Auth, Storage, and Realtime are new products you enable on purpose. They do not appear from MySQL tables.
  • The load uses a direct or session connection, not the transaction pooler on port 6543.
  • We keep a pscale dump until Supabase has passed one application cycle.
02What transfers

What survives the move.

“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.

ItemOutcomeImpactWhat happensMitigation
Tables and rowspartialcriticalpgloader can copy schema and data. People dumping live PlanetScale have reported a 100,000-row query limit, which is why several write-ups dump with pscale first and load from local MySQL.Use pscale database dump for the production snapshot, then pgloader from that dump or a local MySQL seed.
Schema namepartialcriticalpgloader often creates a schema named after the MySQL database. Supabase apps expect public.ALTER SCHEMA ... RENAME TO public, or tell pgloader to rename, then confirm search_path.
TypespartialcriticalMySQL AUTO_INCREMENT, TINYINT, unsigned ints, enums, and zero dates do not have identical Postgres types.Create the intended Postgres schema first if you use an ORM, then load data only, or review every pgloader cast.
Row Level SecuritymanualcriticalImported tables are not a Supabase-secure default. One public migration write-up enables RLS after load so tables are not open through the anon key.Enable RLS and write policies before you ship a client that uses the anon or service role keys.
Roles and GRANTsmanualcriticalSupabase uses postgres, authenticator, anon, authenticated, and service_role. pgloader objects may not be owned by the role your app uses.Set schema ownership and grants after load. Test as anon and as authenticated, not only as postgres.
Auth, Storage, and RealtimelosthighPlanetScale has no Supabase Auth users, Storage buckets, or Realtime publications to import.Stand up those products separately if the app needs them. Do not pretend MySQL users are auth.users.
PlanetScale branches and deploy requestslosthighVitess branches do not become Supabase branches.Use Supabase branching only if the plan includes it, and keep schema in versioned migrations.
Connection poolingmanualcriticalThe transaction pooler on port 6543 is the wrong target for pgloader and for session features such as SET and prepared statements during load.Use port 5432 or the session pooler for the import. Point the app at the pooler only after you test the driver.
ORM dialectmanualcriticalMySQL SQL and relationMode settings do not run unchanged on Postgres.Change the provider, generate a Postgres schema, and run the app suite against a staging Supabase project.
BackupsmanualhighSupabase backup access depends on plan. A free project is not a substitute for the PlanetScale dump you just made.Keep the pscale dump and confirm which Supabase backups you can restore before cutover.
SSL to PlanetScalemanualhighDirect pgloader connections have failed on PlanetScale TLS for some operators. pscale connect to 127.0.0.1:3306 is a documented workaround.Prefer a dump, or tunnel with pscale connect so pgloader talks to localhost.
Query insights and Vitess-specific SQLlostmediumInsights history and Vitess-only behavior stay behind.Retest hot queries on Postgres EXPLAIN and keep the dump.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
PlanetScale Vitess databaseSupabase Postgres databasemanualCreate the project first.
MySQL database namepublic schematransformRename after pgloader if needed.
Table rowspublic tablestransformLoad from a pscale dump when live selects hit the 100,000-row cap.
AUTO_INCREMENTserial or identitytransformReset sequences.
TINYINT / unsigned / enumapproved Postgres typestransformDo not accept silent text casts on money or status columns.
Table privilegesRLS plus grants to anon/authenticatedmanualEnable RLS before exposing keys.
pscale dumplocal MySQL or pgloader sourcetransformKeep the dump immutable.
Application DATABASE_URLSupabase URI after verificationmanualSession versus transaction pooler is a separate choice.
PlanetScale passwordNo destinationunsupportedRotate and stop using it after rollback closes.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Hold a Vitess dump that does not depend on a live 100,000-row SELECT.

  1. Confirm the engine is Vitess and record table counts.
  2. Run pscale database dump for the production branch.
  3. Hash the dump directory and store it read-only.
  4. Create the Supabase project on a plan that can hold the data and the backups you need.
  5. Decide whether Auth or Storage will exist at all.

Proof to capture: A dated manifest lists dump hashes, counts, the Supabase plan, and the RLS decision.

Transformation · pscale database dump plus pgloader

Dump then pgloader

Avoid PlanetScale live row caps and TLS surprises.

  1. Dump the branch.
  2. Seed local MySQL if you cannot point pgloader at the dump directly.
  3. Load into Supabase with quote identifiers and a rename to public.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

Transformation · SQL editor or migration files

Supabase grants and RLS

Stop the Data API from exposing imported tables.

  1. Rename the schema to public if needed.
  2. Grant the roles the app actually uses.
  3. Enable RLS and add the first policies before you ship a browser key.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

05Handle with care

The things most likely to hurt.

These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.

Import

Live SELECT stops at 100,000 rows

criticalpossible likelihood

pgloader against live PlanetScale reports a row-count error or a short table.

Consequence
Supabase is missing most of the table.
Mitigation
Dump with pscale and load from that snapshot.

Stop if: Any scoped table is shorter than the PlanetScale count.

Pilot

Tables land outside public

criticalpossible likelihood

The SQL editor is pointed at public while data sits in another schema.

Consequence
The app looks empty.
Mitigation
Rename the schema and check search_path.

Stop if: The app cannot see a table that pgloader reported as loaded.

Verification

Anon key can read imported rows

criticalpossible likelihood

RLS is off and a table is in the API.

Consequence
Customer data is public.
Mitigation
Enable RLS and test as anon before you ship the key.

Stop if: Anon can read a restricted table.

06Precise timeline

Do the work in this order.

Estimate forOne Vitess database moving into a new Supabase project with RLS enabled before client traffic
Total elapsed10–22 business days
Active work36–80 hours
BufferAdd time for large dumps, Auth work, or a wide ORM rewrite.
01
Days 1–3Inventory and plan2–3 days
02
Days 3–6Dump2–3 days
03
Days 6–14Load and harden5–9 days
04
Days 14–18Freeze and switch1–3 days
05
Days 17–22Observe and close4–6 days
  1. Days 1–3 · inventory

    Inventory and plan

    6–12 hours active2–3 days elapsedSchema review waiting
    • Confirm Vitess.
    • List types, row counts, and whether Auth is in scope.
    • Pick dump-then-load.

    Depends on: PlanetScale and Supabase admin access

    Stop / go checkpoint

    Dump?

    Go when: Engine, size, and RLS plan are signed.

    Stop when: The source is PlanetScale Postgres or Auth scope is unclear.

  2. Days 3–6 · backup

    Dump

    4–10 hours active2–3 days elapsedDump runtime waiting
    • Run pscale database dump.
    • Hash files.
    • Create the Supabase project.

    Depends on: Approved inventory

    Stop / go checkpoint

    Load?

    Go when: Dump counts match PlanetScale.

    Stop when: The dump is short.

  3. Days 6–14 · pilot

    Load and harden

    14–32 hours active5–9 days elapsedpgloader runtime waiting
    • Load into a staging project.
    • Rename to public.
    • Enable RLS and run the app suite.

    Depends on: Verified dump

    Stop / go checkpoint

    Cut over?

    Go when: Counts, types, RLS, and app tests pass.

    Stop when: Anon can read restricted data or a type is wrong.

  4. Days 14–18 · cutover

    Freeze and switch

    6–16 hours active1–3 days elapsedWrite freeze waiting
    • Pause PlanetScale writes.
    • Take a final dump and load production.
    • Switch the app URL.

    Depends on: Passed pilot

    Stop / go checkpoint

    Open writes?

    Go when: Supabase is the only writer and counts match.

    Stop when: A table is short or a writer is still on PlanetScale.

  5. Days 17–22 · observe

    Observe and close

    6–10 hours active4–6 days elapsedApplication cycle waiting
    • Watch errors, RLS, and pooler behavior.
    • Keep the dump.

    Depends on: Verified cutover

    Stop / go checkpoint

    Close rollback?

    Go when: No trigger fires through one cycle.

    Stop when: Data or RLS regresses.

07The point of change

Cut over with a way back.

Go live

Cutover

Make Supabase the only writable database after a frozen dump and load.

Recommended window: A write freeze long enough for dump, load, and count checks.

  1. Pause PlanetScale writes.
  2. Take a final pscale dump and hash it.
  3. Load production with the approved pgloader path.
  4. Rename to public, reset sequences, enable RLS, and reconcile counts.
  5. Switch DATABASE_URL and keep PlanetScale read-only.

Proof to capture: Supabase serves the app. Counts match. Anon cannot read restricted tables.

Return to safety

Rollback

Return writes to PlanetScale without losing reviewed Supabase-era rows.

Deadline: Before you delete the PlanetScale database or the dump.

  1. Stop writes to Supabase.
  2. Point the app back at PlanetScale.
  3. Export Supabase rows written after cutover.
  4. Apply only reviewed rows to PlanetScale.
  5. Run the same app checks on PlanetScale.

Proof to capture: PlanetScale is writable again and Supabase is not receiving production writes.

Rollback immediately when
  • Short table
  • Schema not public
  • RLS failure
  • Wrong pooler port in production
  • App query failure
08Verification report

Prove the migration worked.

Every blocking check must pass. Capture the evidence before cleanup begins.

0%
Interactive report preview0 / 8 checks passed
PassIDCheckMethodExpected resultEvidence
V-01Blockingpscale snapshotCompare dump table counts to PlanetScale.No scoped table is short.Dump manifest.
V-02BlockingLoaded countsCount rows in the intended Supabase schema.Counts match the dump.Count sheet.
V-03Blockingpublic search_pathList tables as the app role.The app sees every scoped table in public.\dt output.
V-04BlockingCast reviewInspect TINYINT, enum, unsigned, and datetime columns.Approved types only.Type ledger.
V-05BlockingRLSQuery as anon and as authenticated.Restricted tables deny anon.Policy test.
V-06BlockingPooler portInspect the import URL and the app URL.Import did not use transaction pooler 6543. App URL is the approved one.Config audit.
V-07BlockingORM suiteRun application tests against Supabase.No MySQL-only SQL on a critical path.Test report.
V-08BlockingDump and rollback URLConfirm the dump exists and PlanetScale still accepts the old URL.You can point the app back.Rollback note.
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: One application cycle, RLS tests passing, and seven stable days.

  1. Archive the pscale dump and pgloader log.
  2. Revoke PlanetScale passwords used for the move.
  3. Remove temporary SQL functions and migration users.
  4. Keep PlanetScale until billing and rollback close.
  5. Document direct versus pooler URLs.
  6. Schedule the next Supabase backup and policy review.
10Experiences from the field

When the plan met reality.

First-hand accounts are preferred. Vendor case studies are labeled, and every note below is an editorial paraphrase—follow the link for the full context.

First-person accountHein Zaw · heinzaw.dev

Migrating from PlanetScale (free tier) to Supabase (free tier)2024

Hein Zaw left PlanetScale Hobby after the $47 Scaler Pro jump. He tunneled with pscale connect, loaded MySQL into local Postgres with pgloader, then rewrote Drizzle from mysqlTable to pgTable. Inserts needed returning() because Postgres does not give MySQL insertId. He dumped data-only SQL with sequence setval lines, tested two files locally, then applied schema and data on Supabase. He flags the 500 MB Supabase free storage cap versus PlanetScale's old 5 GB, and the lack of free restore access.

What they recommend
  • Load into local Postgres and pass the application test suite before you touch the Supabase project.
  • Keep sequence setval lines from pg_dump. Postgres will not continue MySQL autoincrement for you.
Worth noticing
  • The ORM rewrite, not pgloader, was the long pole. Last-insert-id and datetime types all changed.
  • Supabase free tier is not a feature match for PlanetScale Hobby storage, backups, or branching.
First-person accountAbhishek Saha · ajaxtown.com

Migrating from Planetscale to Supabase with automated backups2024

Abhishek Saha left PlanetScale Hobby for cost. A Colab notebook could not open a TLS PlanetScale session. pgloader worked after pscale connect exposed MySQL on 127.0.0.1:3306. He pushed the Prisma schema first, then loaded data only into public and set the schema owner to postgres to avoid permission errors. Because Supabase free tier withholds backup restore, he added a cron pg_dump of the public schema.

What they recommend
  • Create tables with your ORM first, then tell pgloader to copy data only into public owned by postgres.
  • If you need restores on Supabase free tier, schedule your own pg_dump. The product keeps backups you cannot download.
Worth noticing
  • PlanetScale TLS blocked the notebook path. A local pscale tunnel unblocked pgloader.
  • Losing first-party restore access is a product gap you have to replace, not a footnote.
Sources and evidence

Verify against the primary material.

Platform behavior changes. Check these sources and the review dates above before executing a production migration.

  1. Supabase: migrating to SupabaseAccessed 2026-10-29
  2. PlanetScale: pscale database dump (Vitess)Accessed 2026-10-29
  3. Supabase: connect with PostgresAccessed 2026-10-29