Migrating from PlanetScale to Layerbase
Short version: you move to plain managed MySQL, or MariaDB if you prefer, and because a PlanetScale Vitess database speaks the MySQL wire protocol your tables, indexes, and queries port directly. The copy is one service token in the wizard at layerbase.com/migrate/planetscale, which mints a short-lived read-only password, copies schema and data server-side, then deletes the password; by hand it is a mysqldump with --set-gtid-purged=OFF. The app-side change is the driver: @planetscale/database gives way to mysql2 or whatever standard MySQL client you already use elsewhere, which is one line on a Node runtime. What does not port is PlanetScale's branching and deploy-request workflow, since that is platform machinery rather than data. What you get back is real foreign keys, which Vitess never enforced.
PlanetScale built a genuinely good product on Vitess: horizontal scale, branching, online schema changes. They have since added a Postgres offering alongside it, so "PlanetScale" now means two engines rather than one. But the pricing has moved steadily up-market. The free Hobby tier was retired in 2024, and every Vitess SKU is a 3-node HA cluster, so the smallest MySQL database on the price list is $39 a month before storage, backups, egress, or extra replicas. For a side project that used to run on Hobby, that is a real bill where there was none.
The good news: on the Vitess side it is still MySQL under the hood, and your data is portable either way. PlanetScale bills a cluster size plus storage, backups, egress, and any extra replicas or dedicated PgBouncer on top; if you would rather pay one flat monthly figure that includes those, moving to Layerbase Cloud is mostly a data copy and a driver swap. You also get one thing back that PlanetScale takes away: real foreign keys.
PlanetScale rates verified 2026-08-26 from planetscale.com/pricing: no free tier, Vitess starts at PS-10 non-metal for $39 a month as a 3-node HA cluster, and the $5 PS-5 entry price everyone quotes is a single-node Postgres SKU, not a MySQL one.
This guide covers what actually changes, how to stand up MySQL locally to test against, how to copy your data (one service token, or by hand), and the small app-side change. It is written for a PlanetScale MySQL database. If yours is PlanetScale Postgres, that is standard Postgres and the move is the same shape but simpler: a connection-string copy into Layerbase Postgres, with no driver swap and no foreign-key story to unwind.
Just want it done? Start at layerbase.com/migrate/planetscale. You sign in, the wizard opens with PlanetScale already selected, and it copies your schema and data into managed MySQL (or MariaDB) using a short-lived read-only password it mints and then deletes: read-once, nothing written back to PlanetScale. For a PlanetScale Postgres database, pick the Other Postgres source instead and paste its connection string. The rest of this post is that same migration explained step by step, plus the manual path.
Contents
- What Actually Changes
- Set Up MySQL Locally with the Layerbase CLI
- Copy Your Data
- Swap the Driver in Your App
- Foreign Keys, Back Again
- What to Test
- The Managed Path: Layerbase Cloud
- FAQ
What Actually Changes
A PlanetScale Vitess database speaks the MySQL wire protocol, so most of your stack does not care where the database lives. Three things are worth knowing about:
- The connection. PlanetScale pushed the serverless HTTP driver (
@planetscale/database) for edge runtimes. Layerbase MySQL is a standard MySQL server, so you connect with a normal client (mysql2,mysqlclient, JDBC, whatever you already use elsewhere) over a TLS connection string. If you're on a normal Node/Python/Go server, this is a one-line change. If you're on a serverless/edge runtime that needs HTTP, see the driver section below. - Vitess constraints go away. PlanetScale runs Vitess, which historically did not support foreign keys, so most PlanetScale apps disabled them (Prisma's
relationMode = "prisma", Drizzle without FK constraints) and enforced relationships in application code. Standard MySQL has foreign keys, so you can turn them back on. - Branching is a different model. PlanetScale's database branching + deploy requests are specific to their platform. Layerbase has its own database branching, but if you lean heavily on PlanetScale's schema-change workflow, that is the piece that does not port one-to-one. Plain
ALTER TABLEworks as it does on any MySQL.
Everything else (your tables, indexes, queries, stored data) is standard MySQL and copies straight over.
Set Up MySQL Locally with the Layerbase CLI
Before touching production, run MySQL locally and migrate into it so you can compare. The fastest way is the Layerbase CLI (formerly SpinDB): one CLI, no Docker, no manual install. (What is the Layerbase CLI?)
Install the Layerbase CLI:
npm i -g layerbase # npm
pnpm add -g layerbase # pnpmCreate and start a MySQL instance:
lbase create planetscale-migration -e mysql --startOpen a shell on it:
lbase connect planetscale-migrationYou're now in a mysql> shell against a local MySQL, ready to receive a dump.
Copy Your Data
Two paths, depending on how much you want to automate.
The managed wizard (one service token)
On Layerbase Cloud, the create flow does the copy for you. Choose Migrating from another platform, pick PlanetScale, and paste a service token and its ID (PlanetScale dashboard, then Settings, then Service tokens; the token needs an org-level read-databases permission). It lists your databases; for the one you pick it mints a short-lived read-only password on a branch, copies the schema and data into a fresh managed MySQL, then revokes the password. You never handle a connection string, and nothing is left behind on the PlanetScale side.
By hand with mysqldump
If you'd rather do it yourself (or into the local instance above), mysqldump works, with two Vitess-specific flags:
mysqldump \
--host=<your-db>.<region>.psdb.cloud \
--user=<username> --password=<password> \
--single-transaction \
--set-gtid-purged=OFF \
--no-tablespaces \
<database_name> > dump.sql--single-transaction gives a consistent snapshot without locking. --set-gtid-purged=OFF is required because Vitess reports GTID state that a plain MySQL import doesn't want. Then load it into your target:
# Local instance
mysql -h 127.0.0.1 -P $(lbase port planetscale-migration) -u root <database_name> < dump.sql
# Or a managed Layerbase MySQL (connection string from Quick Connect)
mysql -h <your-host>.cloud.layerbase.dev -P <port> -u layerbase -p<password> --ssl <database_name> < dump.sqlGrab a PlanetScale connection password from the dashboard's "Connect" panel (or mint a dedicated one for the dump and delete it afterward).
Swap the Driver in Your App
If your app connects with a normal MySQL client already, you only change the connection string. The change that matters is for apps using PlanetScale's serverless HTTP driver.
Before (PlanetScale serverless driver):
import { connect } from '@planetscale/database'
const conn = connect({ url: process.env.DATABASE_URL })
const results = await conn.execute('select * from users where id = ?', [id])After (standard MySQL with mysql2):
import mysql from 'mysql2/promise'
const pool = mysql.createPool(process.env.DATABASE_URL) // mysql://user:pass@host:port/db?ssl={"rejectUnauthorized":true}
const [rows] = await pool.execute('select * from users where id = ?', [id])Same SQL, same placeholders. If you're on Drizzle, switch the driver from drizzle-orm/planetscale-serverless to drizzle-orm/mysql2. On Prisma, change the datasource provider to mysql and point url at the new connection string.
A note on edge runtimes: @planetscale/database exists because some edge platforms can't open raw TCP connections. If you deploy to a Node runtime (most apps, including Next.js with the Node runtime), mysql2 is the simpler choice. If you specifically need HTTP-based access from an edge function, keep that call path behind a small API route on a Node runtime that talks to MySQL over TCP.
Foreign Keys, Back Again
This is the upgrade people forget. Because Vitess didn't enforce foreign keys, most PlanetScale apps turned relational integrity off:
// PlanetScale: relationships enforced in app code only
datasource db {
provider = "mysql"
relationMode = "prisma"
}On standard MySQL you can drop relationMode = "prisma" and let the database enforce foreign keys again, which means no more orphaned rows from a missed application-level check:
ALTER TABLE orders
ADD CONSTRAINT fk_orders_user
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;If you previously relied on relationMode = "prisma" indexes, real foreign keys create the backing indexes for you. This is a quiet but real correctness win from leaving Vitess.
What to Test
- Run your test suite against the local MySQL. Standard MySQL is a superset of what Vitess allowed, so queries that worked on PlanetScale work here. The things to watch are anywhere you worked around Vitess.
- Check any raw SQL that assumed no foreign keys. Re-enabling FKs can surface inserts that were silently creating orphans. That's the constraint doing its job; fix the insert order or add the missing parent rows.
- Confirm connection pooling. PlanetScale's HTTP driver hid connection management. With
mysql2use a pool and set a saneconnectionLimit; serverless deployments should use a small pool per instance or a pooler. - Re-check
AUTO_INCREMENTvalues. After a dump/restore, confirm sequences resume above your max id (a fresh import usually handles this, but verify on tables you write to immediately).
The Managed Path: Layerbase Cloud
The do-it-yourself version above is worth running once to understand what moved. When you want managed MySQL you don't operate yourself, Layerbase Cloud runs it with TLS, backups, and a dashboard, on flat per-instance pricing rather than PlanetScale's metered model. Prefer MariaDB? It's a click away and the same migration path applies (a MySQL dump restores straight into MariaDB).
And the migration is the wizard described above: Migrating from another platform, paste a PlanetScale service token, pick the database, and it copies everything across server-side and hands you a standard MySQL connection string for your app. Off the three-node cluster minimum, onto an engine you can read and run.
FAQ
Do I have to take the app down to move?
Not for the copy. Both paths read from PlanetScale and write nothing back: the wizard uses a short-lived read-only password on a branch, and the manual mysqldump runs with --single-transaction for a consistent snapshot without locking. The only moment that needs care is the cutover, because writes that land on PlanetScale after the dump started will not be in the copy. Rehearse against the local instance, then take a short write pause when you swap the connection string.
Does all of my data come across?
Everything that is actually MySQL does: tables, indexes, rows, views, and stored routines all restore from the dump. What does not come across is the PlanetScale platform layer around it, meaning branches, deploy requests, and connection passwords. Check AUTO_INCREMENT values on tables you write to immediately after the restore, since that is the one detail worth verifying rather than assuming.
What replaces deploy requests and PlanetScale branching?
Layerbase has database branching, so the branch-per-preview habit survives, but the deploy-request review flow around schema changes does not port one-to-one. On standard MySQL a schema change is ALTER TABLE, run through whatever migration tool your framework already ships. If that review workflow is the main reason you are on PlanetScale, weigh it before moving, because it is the piece you would be rebuilding.
I deploy to an edge runtime. Can I still connect?
@planetscale/database exists because some edge platforms cannot open raw TCP connections, so this is worth checking before you start. On a Node runtime, which includes most Next.js apps, mysql2 connects directly and the swap is trivial. If a specific path genuinely runs at the edge, put a small API route on a Node runtime in front of it and let that route talk to MySQL over TCP.
Is this cheaper, and is there still a free option?
PlanetScale has no free tier since Hobby was retired, and because every Vitess SKU is a 3-node HA cluster the smallest MySQL database on their list is $39 a month before storage, backups, egress, and replicas. Layerbase MySQL is a flat per-instance price with those included, and the pricing page has the current numbers. If you spot a $5 PlanetScale figure quoted somewhere, that is PS-5 on the Postgres side, a different product from the Vitess database this post is about.
Wrapping Up
Leaving PlanetScale is mostly mechanical: it's MySQL, so your tables and queries port directly. The real changes are swapping the serverless driver for a standard MySQL client (one line on a Node runtime) and, if you want it, turning foreign keys back on now that you're off Vitess. The data copy is either one service token through the managed wizard, or a mysqldump with --set-gtid-purged=OFF.
Manage your local MySQL instance with the Layerbase CLI:
lbase stop planetscale-migration # Stop the server
lbase start planetscale-migration # Start it again
lbase url planetscale-migration # Print the connection URL
lbase list # See all your instancesThe Layerbase CLI handles 20+ database engines, so your MySQL can sit next to Postgres, Redis, or Meilisearch while you verify the move. Layerbase Desktop wraps the same thing in a GUI on macOS. For the MySQL-vs-MariaDB decision, see MySQL vs MariaDB.
Keep reading
- MariaDB 13.0 and 12.3 LTS on Layerbase: which line to pickMariaDB 13.0 and 12.3 are now offered on Layerbase alongside 10.11, 11.4 and 11.8, and the older lines have been refreshed to their current patches. Here is what actually shipped in 13.0, what 12.3 changed, why one of them is the LTS and the other is not, and what happens to databases already on 11.8 (nothing).
- Free MySQL hosting: start on MariaDB, upgrade to MySQL when you need itMySQL is not on the Layerbase free tier and MariaDB is, and they speak the same wire protocol. Here is the dump command that survives a restricted source account, every error the restore actually throws, what MySQL keeps that MariaDB does not, and the route back to real MySQL later.
- Moving a MySQL database off cPanel shared hostingYour database is on a shared host that binds MySQL to localhost, runs a version you did not pick, and keeps the only backup. Here is the whole move: getting a dump that is actually complete, landing it on managed MariaDB, repointing the app, and the parts of shared hosting you will genuinely miss.
- PlanetScale has no free tier. Here is where the $5 databases live now.The Hobby plan closed in March 2024 and PlanetScale docs now say plainly that there is no free plan. Here is what replaced it, what $5 a month actually buys in 2026, and when paying PlanetScale is still the right call.