← All playbooks
Databases migration

PostgreSQL → PlanetScale

Move a PostgreSQL database onto PlanetScale Postgres with pg_dump and pg_restore. Use --no-owner and --no-privileges. Size the PlanetScale disk to at least 150 percent of the source. Pause writes if you are not using the WAL streaming path.

Typical timeline6–16 business days22–55 hours active work
Statusneeds review
Source profilePostgreSQL backup-dump documentation reviewed 2026-10-29
Destination profilePlanetScale Postgres pg_dump import guide reviewed 2026-10-29
Documentation date
Review window
Evidence4 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

PostgreSQL

Reasons to stay
  • Powerful SQL, extensions, integrity, and open governance support demanding workloads
  • A mature data model and query ecosystem supports serious production workloads
Reasons to leave
  • Tuning, upgrades, and sophisticated features require experienced operators
  • Schema design, migration, tuning, and operations require specialist attention
New platform

PlanetScale

What gets better
  • 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
What gets worse
  • 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
Best of the move

PlanetScale: Branching, deploy requests, and Metal performance make Vitess MySQL or managed Postgres appealing for production apps. This removes a major source-side concern: Tuning, upgrades, and sophisticated features require experienced operators.

Worst of the move

What you lose: Powerful SQL, extensions, integrity, and open governance support demanding workloads. What you inherit: Engine choice, Vitess limits, and paid-plan economics change how you dump, branch, and cut over.

Jump to a section
01At a glance

Know the shape of the move.

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

This timeline assumes

  • The destination is PlanetScale Postgres, not Vitess MySQL. A Postgres-to-MySQL move is a different project with type conversion.
  • PlanetScale calls pg_dump the simple path for smaller databases that can take downtime. Larger or online moves use their WAL streaming or assessment path.
  • PlanetScale asks for disk at least 150 percent of the source. Network-attached storage can raise minimum disk size. Metal sizes are chosen at create time.
  • You have pg_dump, pg_restore, and psql, plus a PlanetScale Postgres connection from the console.
  • We keep the source writable-ready until PlanetScale 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
Schema and datapartialcriticalpg_dump plus pg_restore copies tables, data, and most schema objects. PlanetScale tells you to use --no-owner and --no-privileges because source owners and GRANTs do not apply.Dump custom format for large databases, restore with --no-owner --no-privileges, then apply the PlanetScale roles you actually want.
Ownership and privilegeslostcriticalThose flags drop owners and grants on purpose.Recreate roles, default privileges, and app GRANTs on PlanetScale after restore.
ExtensionspartialcriticalOnly extensions PlanetScale Postgres allows will restore. Others fail the restore or create missing objects.List source extensions and confirm each one on PlanetScale before you dump production.
Roles, memberships, and password hashesmanualcriticalCluster roles are not a portable dump of login roles.Create PlanetScale roles from the console or SQL you control. Rotate passwords.
Physical replication slots and WAL statelosthighA dump is not a replica. In-flight WAL does not come along unless you use the streaming import path.Freeze writes for dump/restore, or follow PlanetScale's WAL streaming guide instead of this playbook's simple path.
Tablespaces and source pathslostmediumSource tablespaces do not exist on PlanetScale.Dump without tablespace assignments and restore into the default space.
Disk headroommanualcriticalPlanetScale wants about 150 percent of source size. A restore can fail mid-file if the disk is short.Measure source size, set minimum disk or pick Metal large enough, and watch free space during restore.
Connection pooling and serverless sourcesmanualhighDump from a session or direct endpoint. Transaction poolers break pg_dump.Use the source direct URL and a PlanetScale connection from the console, not a pooler, for dump and restore.
Jobs, logical replication, and foreign serversmanualhighCron-like jobs, subscriptions, and FDWs are cluster objects that often fail or need a rewrite.Inventory them and recreate only what PlanetScale supports.
Query performancemanualhighA restore does not tune work_mem, indexes, or statistics for the new host.ANALYZE after restore and run the hot query set before you switch the app.
Source backups and PITRlostmediumOld WAL and snapshots stay on the source.Keep the source until PlanetScale backups are configured and tested.
Application cutovermanualcriticalThe dump is stale the moment writes resume on the source.Freeze writes, restore, verify, then switch DATABASE_URL.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
PostgreSQL databasePlanetScale Postgres databasemanualCreate the PlanetScale database and confirm disk size first.
Schema and tablesSame names after pg_restoreautomaticUse custom format (-Fc) for large databases.
OwnersNo destinationunsupported--no-owner drops them.
PrivilegesPlanetScale GRANTs you apply after restoremanual--no-privileges drops them.
ExtensionsAllowed PlanetScale extensionstransformFail the pilot if a required extension is missing.
SequencesRestored sequencestransformInsert a row and confirm the next value.
RolesPlanetScale rolesmanualCreate and rotate.
Direct source URLpg_dump sourcetransformAvoid transaction poolers.
Application DATABASE_URLPlanetScale URL after verificationmanualSwitch only after ANALYZE and app tests.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Hold a source dump and a sized PlanetScale target before you freeze production.

  1. Record database size, table counts, extensions, and jobs.
  2. Create the PlanetScale Postgres database with disk at least 150 percent of source size.
  3. Run PlanetScale's migration assessment if you have it, and decide dump versus WAL streaming.
  4. Take a rehearsal pg_dump --format=custom --no-owner --no-privileges.
  5. Store the dump read-only and hash it.

Proof to capture: A dated manifest lists size, disk, extensions, dump hash, and the chosen import path.

Transformation · pg_dump -Fc

Custom-format dump

Follow PlanetScale's recommended dump flags.

  1. Dump from a direct endpoint.
  2. Use --no-owner --no-privileges --verbose.
  3. Keep a SQL-format dump only if you must restore with psql.

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

Transformation · pg_restore --jobs=4 plus ANALYZE

Parallel restore and ANALYZE

Load faster and refresh statistics.

  1. Restore into the empty PlanetScale database.
  2. Fix any failed extension or object.
  3. ANALYZE and run the hot query set.

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

Disk fills during restore

criticalpossible likelihood

PlanetScale disk is near the source size instead of 150 percent.

Consequence
Restore stops and the target is half-loaded.
Mitigation
Size disk first and watch usage during restore.

Stop if: Free disk cannot finish the restore.

Cutover

Writes continue during dump

criticalpossible likelihood

The app still writes to the source after pg_dump starts and you are not using WAL streaming.

Consequence
PlanetScale is missing those rows.
Mitigation
Freeze writes for the dump/restore path.

Stop if: A production write lands on the source after the freeze.

Pilot

Required extension missing

criticalpossible likelihood

pg_restore errors on an extension PlanetScale does not offer.

Consequence
Functions or indexes the app needs do not exist.
Mitigation
Confirm extensions before production dump.

Stop if: A required extension cannot be created.

06Precise timeline

Do the work in this order.

Estimate forOne PostgreSQL database using PlanetScale's pg_dump path with an approved write freeze
Total elapsed6–16 business days
Active work22–55 hours
BufferAdd time for WAL streaming, large custom-format dumps, or missing extensions.
01
Days 1–3Inventory and size2–3 days
02
Days 3–5Rehearsal dump1–2 days
03
Days 5–10Restore rehearsal3–6 days
04
Days 10–13Freeze, dump, restore1–2 days
05
Days 12–16Observe and close3–5 days
  1. Days 1–3 · inventory

    Inventory and size

    5–10 hours active2–3 days elapsedExtension review waiting
    • Measure size and list extensions.
    • Create PlanetScale with enough disk.
    • Choose dump versus WAL streaming.

    Depends on: Source and PlanetScale admin access

    Stop / go checkpoint

    Dump?

    Go when: Disk, extensions, and freeze window are signed.

    Stop when: A required extension is unavailable or the window is too short.

  2. Days 3–5 · backup

    Rehearsal dump

    3–8 hours active1–2 days elapsedDump runtime waiting
    • Take a custom-format dump.
    • Hash it.
    • Confirm restore tools.

    Depends on: Approved inventory

    Stop / go checkpoint

    Restore?

    Go when: Dump hash and size look complete.

    Stop when: The dump is short or unreadable.

  3. Days 5–10 · pilot

    Restore rehearsal

    8–20 hours active3–6 days elapsedRestore runtime waiting
    • Restore into PlanetScale.
    • Fix grants and extensions.
    • ANALYZE and run app tests.

    Depends on: Verified dump

    Stop / go checkpoint

    Cut over?

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

    Stop when: A required object is missing.

  4. Days 10–13 · cutover

    Freeze, dump, restore

    4–12 hours active1–2 days elapsedWrite freeze waiting
    • Pause source writes.
    • Dump and restore production.
    • Switch DATABASE_URL.

    Depends on: Passed pilot

    Stop / go checkpoint

    Open writes?

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

    Stop when: A row is missing or the source is still writable.

  5. Days 12–16 · observe

    Observe and close

    2–5 hours active3–5 days elapsedApplication cycle waiting
    • Watch errors and slow queries.
    • Confirm PlanetScale backups.

    Depends on: Verified cutover

    Stop / go checkpoint

    Close rollback?

    Go when: No trigger fires through one cycle.

    Stop when: Queries or data regress.

07The point of change

Cut over with a way back.

Go live

Cutover

Make PlanetScale Postgres the only writable database after a frozen dump and restore.

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

  1. Pause application writes on the source.
  2. Take the production custom-format dump.
  3. Restore to PlanetScale with --no-owner --no-privileges.
  4. Apply roles, ANALYZE, and reconcile counts.
  5. Switch DATABASE_URL and keep the source recoverable.

Proof to capture: PlanetScale serves the app. Counts match. The source is not receiving writes.

Return to safety

Rollback

Return writes to the source PostgreSQL without losing reviewed PlanetScale-era rows.

Deadline: Before you drop the source or its PITR window.

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

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

Rollback immediately when
  • Count mismatch
  • Missing extension
  • Disk-full restore
  • App query failure
  • Write landed on the wrong host
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-01BlockingEngine and diskConfirm PlanetScale Postgres and disk at least 150 percent of source size.Postgres engine, enough disk.Console screenshot.
V-02BlockingRow countsCompare source and PlanetScale table counts after the freeze.Every scoped table matches or has a signed exception.Count sheet.
V-03BlockingRequired extensionsList extensions on both sides.Every required extension exists.\dx output.
V-04BlockingSequencesInsert a row on a serial table.The next value is correct.Insert log.
V-05BlockingRoles and grantsConnect as the app role and as a denied role.Least privilege after --no-privileges.Grant sheet.
V-06BlockingQuery suiteRun ANALYZE and the application tests.Hot queries and tests pass.Test report.
V-07BlockingDirect URLsConfirm dump and restore did not use a transaction pooler.Direct endpoints only for dump/restore.Config audit.
V-08BlockingSource still upProve the source accepts the old URL.You can point the app back.Rollback note.
09Post-migration cleanup

Remove the scaffolding safely.

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

  1. Archive the production dump and restore log.
  2. Revoke temporary dump roles.
  3. Keep the source until billing and rollback close.
  4. Document the PlanetScale connection string.
  5. Remove leftover restore users.
  6. Schedule the next backup and extension 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 accountAutumn · useautumn.com

Migrating Autumn to PlanetScale2026

Autumn, an AI billing platform, moved from an unnamed previous PostgreSQL provider onto PlanetScale Metal after pooling, upgrade, and latency problems. Manual replication stalled. PlanetScale support pointed them at the company's pgcopydb fork. The copy and cutover took two days with seconds of downtime. They report query latency falling from about 100 ms to under 10 ms. They do not name the source vendor.

What they recommend
  • Use PlanetScale's pgcopydb fork for an online Postgres cutover instead of inventing your own replication.
  • Measure p99 on the same queries before and after Metal. Autumn's story is latency, not a dump-and-restore diary.
Worth noticing
  • A same-engine Postgres move can finish in days when CDC is already packaged.
  • The source provider is unnamed. Treat the latency numbers as Autumn's, not as a named vendor comparison.
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. PlanetScale: Postgres pg_dump and restoreAccessed 2026-10-29
  2. PlanetScale: Postgres import optionsAccessed 2026-10-29
  3. PlanetScale: Postgres imports with pgcopydbAccessed 2026-10-29
  4. PostgreSQL: SQL dumpAccessed 2026-10-29