Want help planning the move? PlanetScale’s migration services can walk through export, import, and cutover with you.
Overview
The workflow has three parts:- Export your remote D1 database with Wrangler
- Lint and import the SQL dump with
pscale import d1 - Verify row counts, then update your application connection string
Prerequisites
Before you start, install and configure:- PlanetScale CLI — authenticated to your org (
pscale auth login, thenpscale org set <org>) - Cloudflare Wrangler — to export the remote D1 database
- pgloader — required for loading data (used for all import sizes)
psql— PostgreSQL client (version 10 or newer)sqlite3(optional) — helps with deeper verification checks
- A PlanetScale Postgres database with enough storage for the imported data
- A target branch that is empty or disposable (the import replaces schema on that branch)
- Cloudflare credentials for remote export (
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_ID), unless you already have a D1 SQL export file
Step 1: Export D1 with Wrangler
Export your production D1 database to a SQL file:pscale import d1 command uses --input ./d1-export.sql.
Step 2: Lint the export
Review schema and data compatibility issues before touching Postgres:Step 3: Preview the import
Run a dry run on the default branch to lint again, build the import plan, and get a migration ID:my-database is the PlanetScale database (the cluster). --dbname is the Postgres database inside it. If you omit --dbname, data lands in postgres. Use the same --dbname on start and verify.
Use --format json if you want machine-readable output. Save the migration_id from the response.
The command above targets the default branch (main). To target a different branch, include the branch name after the database name:
Step 4: Import into PlanetScale Postgres
Run the import against your PlanetScale database:--force to skip the confirmation prompt in scripts.
Step 5: Verify the import
Compare source and destination row counts and run spot checks:Step 6: Complete and cut over
When verification passes, mark the migration complete:Important notes
ORM migration tables are skipped
pscale import d1 does not import ORM or framework migration bookkeeping tables. These SQLite-only history tables are not valid on Postgres and are excluded from schema conversion and data load.
Examples the linter detects and the importer skips:
- Drizzle —
__drizzle_migrationsand related tables - Prisma —
_prisma_migrations - Knex —
knex_migrations,knex_migrations_lock - Rails —
schema_migrations,ar_internal_metadata - Django —
django_migrations - Sequelize, Flyway, Liquibase, Alembic, TypeORM, Goose — their version/history tables
prisma db pull + a fresh migration, or drizzle-kit push). Do not expect SQLite migration history to carry over.
Run pscale import d1 lint --input ./d1-export.sql to see which tables in your export match these patterns.
Plan for downtime
This path is a point-in-time snapshot. Applications should stop writing to D1 before export, or you accept that rows written after export will not be on PlanetScale.Storage and branch choice
Size your PlanetScale database for the full imported dataset. Run the import on a branch you can replace if something goes wrong.Application table name collisions
If you have an application table namedschema_migrations that is not Rails migration metadata, lint may warn about a name collision. Rename that table in D1 before export, or confirm lint output carefully so real data is not skipped by mistake.

