PlanetScale Best Practices: Schema Changes, Limits and Connections
The PlanetScale defaults that matter in production: safe migrations on main, deploy requests with a real revert plan, queries shaped for Vitess limits, and scoped credentials.
Key takeaways
- Turn on safe migrations for every production branch so schema changes only arrive through reviewed deploy requests with a 30-minute revert window.
- Design queries for Vitess limits of 100,000 rows per query and 20 seconds per transaction.
- Rename columns in two deploys, and give each app a branch-scoped, IP-restricted password.
On this page
The PlanetScale practices that pay off in production are few: enable safe migrations on the production branch, ship every schema change as a deploy request, shape queries for the Vitess limits before they bite, and give each service its own scoped password. Most avoidable PlanetScale outages come from skipping one of those four.
Verified against
PlanetScale Vitess safe migrations, deploy requests, column renames, system limits, Insights and connection strings, against the PlanetScale docs. Checked November 2026.
This page covers PlanetScale's Vitess (MySQL) product. If you are still deciding between PlanetScale and a serverless Postgres, start with Neon vs PlanetScale and come back once the choice is made.
Safe migrations on main is the first switch to flip
Safe migrations is a per-branch setting, and PlanetScale recommends it for every production branch. With it on, DDL sent straight to that branch is rejected, whether it comes from the console, a terminal or a MySQL GUI. Schema changes can only arrive through a deploy request, which means somebody reviews them.
pscale branch safe-migrations enable app main
pscale branch create app add-orders-status
pscale shell app add-orders-status # ALTER TABLE orders ADD COLUMN status ...
pscale deploy-request create app add-orders-status
Under the hood, a deploy builds a ghost table with the new schema and keeps it in sync with production until cutover, so a large ALTER TABLE does not lock the table your app is writing to. Teams that leave safe migrations off "for speed" are trading that guarantee for the ability to run an unreviewed ALTER against production at 5 p.m. on a Friday.
Treat the 30-minute revert as your rollback plan, not a safety net
After a deploy, PlanetScale keeps the old table in sync for 30 minutes so you can revert. After that window, the change is permanent. Three details decide whether the revert actually saves you:
- Instant deployments cannot be reverted. They also take an exclusive metadata lock and kill queries holding locks on the table, so your app needs retry logic. Use them only for changes you would never want to undo.
- Reverts fail on data the old schema cannot hold. Widen
VARCHAR(10)toVARCHAR(50), let users write 40-character values, and the revert has nowhere to put them. - Auto-apply is on by default. Uncheck it for risky changes so the cutover waits for
pscale deploy-request applyat a time you choose. An open gated deploy holds the branch's serial queue, so do not leave it parked overnight.
Renames take two deploys, by design
PlanetScale does not support RENAME for tables or columns. The declarative diff cannot tell a rename from a drop plus an add, and it refuses to guess. The supported path is the same expand-and-contract sequence used on any busy database:
- Add the new column (deploy request one).
- Make the app write both columns, then backfill old rows.
- Switch reads and writes to the new column.
- Drop the old column (deploy request two).
It is slower than one ALTER, and it is the reason renames never take an outage. The same pattern for Postgres is in zero-downtime migrations with expand and contract.
Design queries for the Vitess limits up front
The Vitess limits are not tuning knobs on the managed service. The ones that break real apps, as of November 2026:
| Limit | Value | What hits it |
|---|---|---|
| Rows returned, updated or deleted per query | 100,000 | Unpaginated exports, bulk DELETE |
| Result set size per query | 64 MiB | SELECT * on wide rows with JSON or text |
| Transaction timeout | 20 seconds from open | Long jobs inside one transaction, idle open transactions |
| Autocommit query timeout | 900 seconds | Heavy reporting queries |
| Connection lifetime | May be closed after 24 hours | Pools that never recycle connections |
Where the limits bite: a nightly cleanup like DELETE FROM events WHERE created_at < ... works for months, then the table grows past 100,000 matching rows and the job starts failing. Batch it by primary key from day one. The 20-second clock starts when the transaction opens, so a worker that opens a transaction, calls an external API, then commits will hit it even with fast queries.
What to do instead: paginate reads, batch writes in chunks of a few thousand rows, keep transactions to database work only, and set your pool's max connection lifetime below 24 hours. OLAP mode lifts the row limit for reads, not for updates or deletes, and is meant for occasional exports, not app traffic. Regular analytics belongs in a warehouse.
Use Insights before you add capacity
Insights lists every query pattern with count, latency (p50 and p95 by default, p99 and p99.9 on toggle), rows read and rows returned. Queries running a full table scan carry a warning icon because they are not using an index. Before you resize a cluster, sort by rows read: a single unindexed lookup reading a million rows to return one is the usual cause of a slow database, and an index is cheaper than a bigger cluster.
One password per service, per branch
Each PlanetScale password is scoped to one branch and cannot read other branches. Use that scoping deliberately:
- One password per service and environment, so revoking a leaked credential does not take down everything. Deleting a password disconnects its clients, which can take up to five minutes.
- Restrict by IP to your app servers or bastion. An unrestricted password accepts traffic from anywhere.
- Use replica passwords for read-heavy paths. A replica-type password routes queries to the branch's replicas and read-only regions, nearest first, so reporting and search reads stay off the primary.
- Save it immediately. Passwords are not stored in plaintext; a lost one gets replaced, not recovered.
Questions people ask
What are the most important PlanetScale best practices?
Enable safe migrations on every production branch so schema changes go through reviewed deploy requests. Use the 30-minute revert window deliberately and avoid instant deploys for anything you may undo. Paginate and batch to stay under the 100,000-row and 20-second transaction limits. Give each service its own branch-scoped, IP-restricted password.
How do I rename a column in PlanetScale?
PlanetScale does not support RENAME for columns or tables. Add the new column in one deploy request, have the app write both columns, backfill existing rows, switch reads and writes to the new column, then drop the old one in a second deploy request. Plan for at least two deploys.
The decision, concretely
- Production branch without safe migrations? Turn it on today; the only cost is losing ad-hoc DDL on main.
- Risky change to a hot table? Gated deploy, apply at a quiet hour, watch Insights for the full revert window.
- Job touching more than 100,000 rows? Batch by primary key; do not reach for OLAP mode in app code.
- Slow database? Check Insights for full table scans before you pay for a bigger cluster.
The call we'd make
Start every PlanetScale database with safe migrations on main, a password per service, and batch-sized jobs. That covers most of the production surprises. The one practice teams skip and regret is watching the 30-minute revert window: treat each cutover as a short on-call shift, because after half an hour the only rollback left is another migration.
Sources
Cite this article
Kiril Urbonas (2026). PlanetScale Best Practices: Schema Changes, Limits and Connections. DevOpsNess. https://www.devopsness.com/blog/planetscale-best-practices
[PlanetScale Best Practices: Schema Changes, Limits and Connections](https://www.devopsness.com/blog/planetscale-best-practices) (Kiril Urbonas, DevOpsNess, 2026-11-11)
More from Cloud
Explore more articles in this category
Can You Run SQLite in Production? (2026)
A practical look at when SQLite is a genuinely good production database, where it breaks, and the tooling that makes it viable.
Cloud Security Best Practices: Securing Your AWS Infrastructure
A working AWS security baseline, derived from the actual incidents we've had and the audit findings we've cleared.
How to Reduce Datadog Costs Without Losing Coverage
Datadog bills climb quietly until finance forwards the invoice. Here's the playbook we run to cut spend hard while keeping every signal that matters.
You might have missed
Evergreen posts worth revisiting.