← All playbooks
Databases migration

PlanetScale → Neon

Move a PlanetScale Vitess MySQL database to Neon Postgres with pgloader. Pause writes first. pgloader is a point-in-time copy. Use a direct Neon connection, put the endpoint ID in the password field, and check every type cast before you point the app at Neon.

Typical timeline8–20 business days30–70 hours active work
Statusneeds review
Source profilePlanetScale Vitess dump and connect docs reviewed 2026-10-29
Destination profileNeon migrate-from-PlanetScale pgloader guide 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

Neon

What gets better
  • Serverless PostgreSQL with branching and scale-to-zero creates excellent developer workflows
  • A mature data model and query ecosystem supports serious production workloads
What gets worse
  • Platform-specific branching, compute behavior, and pricing require workload-aware design
  • Schema design, migration, tuning, and operations require specialist attention
Best of the move

Neon: Serverless PostgreSQL with branching and scale-to-zero creates excellent developer workflows. 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: Platform-specific branching, compute behavior, and pricing require workload-aware design.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome12 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 4, Manual rebuild: 5, Not transferred: 3.
Clean0
Partial4
Manual5
Lost3
Mapping route9 of 9 fields have a destination path

This timeline assumes

  • The source is PlanetScale Vitess (MySQL-compatible), not PlanetScale Postgres. A same-engine Postgres move is a different playbook.
  • Neon documents a 1 GB Free plan cap. Larger data needs a paid Neon plan before you load.
  • You can pause application writes. Neon says pgloader will not catch changes after it starts.
  • The machine running pgloader is allowed through PlanetScale IP restrictions and Neon IP allow rules.
  • We keep a pscale dump or logical backup until Neon 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
Schemas and tablespartialcriticalpgloader creates Postgres tables and can put them in a schema named after the MySQL database. That is not always public.Name the Neon database on purpose and rename the schema to public if that is what the app expects.
Row datapartialcriticalpgloader copies rows in one point-in-time run. Writes after start stay on PlanetScale.Freeze writes, then compare counts and a recent-row sample on both sides.
TypespartialcriticalNeon says AUTO_INCREMENT becomes SERIAL, TINYINT becomes BOOLEAN, and DATETIME becomes TIMESTAMP. Other casts follow pgloader MySQL rules unless you override CAST.Dump the source schema, review every mapped type, and add CAST lines for columns you cannot accept as defaults.
Indexes, keys, and sequencespartialcriticalpgloader reports index, primary key, foreign key, and sequence reset work. Failures show up as errors in that summary, not as a silent Postgres app.Read the pgloader summary. Query sequences and foreign keys before you open writes.
PlanetScale branching and deploy requestslosthighVitess branches and deploy requests do not become Neon branches.Create a Neon branch from the migrated primary if you still want branch-per-PR, and stop treating PlanetScale deploy requests as history.
Vitess sharding and connection behaviorlosthighVitess limits, no stored procedures, and connection pooling behavior stay behind.Retest queries that depended on Vitess or on PlanetScale's pooler.
Stored routines and viewsmanualhighPlanetScale Vitess does not give you portable stored procedures. Views may land in an unexpected schema.Recreate views in public and rewrite routines as application SQL or Postgres functions.
Users, roles, and IP allow listsmanualcriticalMySQL users do not become Neon roles. Neon needs its own roles and allow rules.Create least-privilege Neon roles and allow the app and the pgloader host only.
Connection strings and poolingmanualcriticalNeon tells you to disable the pooling toggle for pgloader, embed endpoint=ENDPOINT_ID in the password, and drop channel_binding. The app later uses a Neon pooled string that is not the pgloader string.Keep two strings. Use direct for pgloader. Use the pooled string only after you test the driver.
ORM SQL dialectmanualcriticalPrisma, Drizzle, or Rails SQL written for MySQL will not run unchanged on Postgres.Change the provider, generate migrations against Neon, and run the app test suite before DNS or secret cutover.
pgloader driver bugsmanualhighNeon documents a mysql 255 fell through ECASE error and points at building pgloader from source.Rehearse on a copy. If you hit ECASE, use the documented source build before you touch production.
PlanetScale query insights and backupslostmediumInsights history and Vitess backup windows stay in PlanetScale.Take a pscale dump first and configure Neon backups before you send traffic.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
PlanetScale databaseNeon project databasemanualCreate the Neon database name before pgloader runs.
MySQL schema or database namePostgres schema, often renamed to publictransformDo not leave app search_path pointing at the old name by accident.
AUTO_INCREMENTSERIAL or identitytransformReset sequences after load.
TINYINTBOOLEAN unless you CAST otherwisetransformCheck columns that stored 0/1 as integers you still query as numbers.
DATETIMETIMESTAMPtransformConfirm time zone handling in the app.
Primary and foreign keysPostgres constraintstransformRead the pgloader foreign-key line.
Direct PlanetScale MySQL URLpgloader source with sslmode=requiretransformNeon asks for the Direct connection string, not a pooled one.
Neon console URLpgloader destination with endpoint ID in the passwordtransformRemove channel_binding.
Application DATABASE_URLNeon URL after verificationmanualSwitch only after counts and app tests pass.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Keep a Vitess dump you can restore even if pgloader or Neon fails.

  1. Record table counts, sizes, and the PlanetScale engine (Vitess, not Postgres).
  2. Run pscale database dump against the production branch into an immutable directory.
  3. Export the schema separately so type review does not depend on a live session.
  4. Confirm the pgloader host is allowed on both IP lists.
  5. Create the Neon project and database on a plan that fits the data size.

Proof to capture: A dated manifest lists the dump path, hashes, table counts, and the Neon plan limit.

Transformation · A .load file plus pgloader

pgloader config

Give pgloader a direct, TLS, endpoint-aware destination.

  1. Use the PlanetScale Direct MySQL string with sslmode=require.
  2. Disable Neon pooling and add endpoint=ID to the password.
  3. Drop channel_binding. Add CAST rules you already approved.

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

Transformation · ORM provider plus the application test suite

App dialect change

Stop shipping MySQL SQL to Postgres.

  1. Point a branch at Neon.
  2. Change the provider and regenerate migrations if you use an ORM.
  3. Run queries that used MySQL-only functions.

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.

Cutover

Writes continue during pgloader

criticalpossible likelihood

The app still writes to PlanetScale after pgloader starts.

Consequence
Those rows never reach Neon.
Mitigation
Pause writers, then start pgloader, then reconcile counts.

Stop if: Any production write lands on PlanetScale after the freeze.

Pilot

TINYINT becomes BOOLEAN

highpossible likelihood

A column you treated as an integer is now true/false.

Consequence
Queries and ORMs return the wrong type.
Mitigation
Review every TINYINT and CAST the ones that are not booleans.

Stop if: A critical column changed meaning in the pilot.

Import

Pooled Neon URL used for pgloader

highpossible likelihood

pgloader uses the pooled host or keeps channel_binding.

Consequence
The load fails or stalls.
Mitigation
Use the direct string and the endpoint-password workaround Neon documents.

Stop if: pgloader cannot complete a rehearsal load.

06Precise timeline

Do the work in this order.

Estimate forOne Vitess database that fits a planned write freeze and a Neon plan large enough for the copy
Total elapsed8–20 business days
Active work30–70 hours
BufferAdd time for large tables, the pgloader ECASE bug, or a wide ORM rewrite.
01
Days 1–3Inventory and types2–3 days
02
Days 3–5Dump and allow lists1–3 days
03
Days 5–12pgloader rehearsal4–8 days
04
Days 12–16Freeze and copy1–3 days
05
Days 15–20Observe and close4–6 days
  1. Days 1–3 · inventory

    Inventory and types

    6–12 hours active2–3 days elapsedSchema review waiting
    • Confirm Vitess, not PlanetScale Postgres.
    • List tables, types, and MySQL-only SQL.
    • Pick CAST overrides.

    Depends on: PlanetScale and Neon admin access

    Stop / go checkpoint

    Dump?

    Go when: Engine, size, and type map are signed.

    Stop when: The source is PlanetScale Postgres or the Neon plan is too small.

  2. Days 3–5 · backup

    Dump and allow lists

    4–8 hours active1–3 days elapsedDump and IP rules waiting
    • Take a pscale dump.
    • Allow the pgloader host on both sides.
    • Create the Neon database.

    Depends on: Approved inventory

    Stop / go checkpoint

    Pilot?

    Go when: The dump hashes and the Neon project exist.

    Stop when: The dump is incomplete.

  3. Days 5–12 · pilot

    pgloader rehearsal

    10–28 hours active4–8 days elapsedpgloader runtime waiting
    • Run pgloader against a Neon branch.
    • Fix schema name, types, and sequences.
    • Run the app test suite.

    Depends on: Verified dump

    Stop / go checkpoint

    Cut over?

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

    Stop when: A critical type or query is wrong.

  4. Days 12–16 · cutover

    Freeze and copy

    6–16 hours active1–3 days elapsedWrite freeze waiting
    • Pause PlanetScale writes.
    • Run production pgloader.
    • Switch the app URL after reconciliation.

    Depends on: Passed pilot

    Stop / go checkpoint

    Open writes?

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

    Stop when: A row is missing or a writer is still on PlanetScale.

  5. Days 15–20 · observe

    Observe and close

    4–6 hours active4–6 days elapsedApplication cycle waiting
    • Watch errors, latency, and sequences.
    • Keep the PlanetScale dump.

    Depends on: Verified cutover

    Stop / go checkpoint

    Close rollback?

    Go when: No trigger fires through one cycle.

    Stop when: Data or auth queries regress.

07The point of change

Cut over with a way back.

Go live

Cutover

Make Neon the only writable database after a frozen pgloader copy.

Recommended window: A low-write window long enough for pgloader plus count checks.

  1. Pause application and job writes to PlanetScale.
  2. Take a final pscale dump.
  3. Run the approved pgloader config against production Neon.
  4. Reconcile counts, sequences, and a recent-row sample.
  5. Switch DATABASE_URL and keep PlanetScale read-only.

Proof to capture: Neon serves the app. Counts match. No writer is still on PlanetScale.

Return to safety

Rollback

Return writes to PlanetScale without losing Neon-era rows you still want.

Deadline: Before you drop the PlanetScale branch or the pscale dump.

  1. Stop writes to Neon.
  2. Point the app back at PlanetScale.
  3. Export Neon 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 Neon is not receiving production writes.

Rollback immediately when
  • Count mismatch
  • Type or sequence error
  • App query failure
  • Write landed on the wrong host
  • pgloader did not finish
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-01BlockingVitess sourceConfirm the PlanetScale engine is Vitess/MySQL before you run pgloader.Source is MySQL-compatible Vitess.Console screenshot.
V-02BlockingRow countsCompare table counts on PlanetScale and Neon after the freeze.Every scoped table matches or has a signed exception.Count sheet.
V-03BlockingCast reviewDiff source schema against Neon information_schema for TINYINT, DATETIME, and AUTO_INCREMENT columns.Every critical column has the approved type.Type ledger.
V-04BlockingSequences and foreign keysInsert a row and inspect pgloader's key summary.New IDs continue and FKs hold.pgloader summary and insert log.
V-05BlockingORM and queriesRun the application test suite against Neon.No MySQL-only SQL remains on a critical path.Test report.
V-06BlockingDirect versus pooled URLsConfirm pgloader used the direct endpoint-password URL and the app uses the intended Neon URL.Two documented strings, no leftover PlanetScale URL in production.Config audit.
V-07BlockingRoles and IP allowConnect as the app role and as a denied IP.Least privilege and denied IPs fail.Access test.
V-08BlockingDump restoreProve the pscale dump exists and the PlanetScale branch still accepts a rollback URL.You can point the app back.Dump path and rollback note.
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: One application cycle, Neon backups configured, and seven stable days.

  1. Archive the pscale dump and the pgloader log.
  2. Revoke the PlanetScale password used by pgloader.
  3. Remove the pgloader host from allow lists you no longer need.
  4. Keep PlanetScale until billing and rollback close.
  5. Document the Neon direct and pooled URLs.
  6. Schedule the next Neon backup and role 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 accountIshan Anand · DEV Community

Migrating your data from PlanetScale to Neon2024

Ishan Anand wrote a pgloader walkthrough after PlanetScale ended its free tier. He treats Neon as a serverless Postgres alternative, not a MySQL clone, and streams schema plus rows with a .load file. The Neon password must include endpoint=ID. He then checks tables in the Neon SQL editor. He notes leftover MySQL-only features still need app changes after the load.

What they recommend
  • Put the Neon endpoint ID in the password field and drop the pooled host before you run pgloader.
  • Compare row counts and a few application queries after the load. Do not stop at a green pgloader summary.
Worth noticing
  • The Hobby-plan shutdown, not a feature gap, is what sent many small apps to Neon.
  • pgloader moves the relational shape. MySQL-only SQL still has to be rewritten.
First-person accountKoko · Medium

Migrating from PlanetScale to Neon, using Prisma and pgloader2024

Koko moved Prisma MySQL apps off PlanetScale Hobby onto Neon. Prisma db push created the Postgres schema, including a schema=DB_NAME query param so tables did not land in public. pgloader then copied data only through a pscale connect tunnel, because a direct aws.connect.psdb.cloud URL failed without TLS. The Neon URL needed endpoint=ID in the password, no -pooler host, and quote identifiers for camelCase columns.

What they recommend
  • Push the Prisma Postgres schema first, then run pgloader with data only and quote identifiers.
  • Use pscale connect as a local MySQL tunnel if pgloader cannot satisfy PlanetScale TLS on the public host.
Worth noticing
  • A raw pgloader URL against PlanetScale often fails on TLS before any rows move.
  • Prisma schema= and Neon endpoint= both have to be right, or you load the wrong schema on the wrong compute.
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. Neon: migrate from PlanetScale MySQL with pgloaderAccessed 2026-10-29
  2. PlanetScale: pscale database dump (Vitess)Accessed 2026-10-29
  3. PostgreSQL: SQL dumpAccessed 2026-10-29