Tutorials / Backend integrations / Set up PlanetScale (MySQL)
📝 Written ● Beginner Updated 2026-05-13

Set up PlanetScale (MySQL)

Serverless MySQL on Vitess — the same database Slack, GitHub, and YouTube run on. Non-blocking schema changes through deploy requests. Sign up, create a database, connect.

When to pick PlanetScale

0
  • Pick PlanetScale when — your stack/team already uses MySQL, you want non-blocking schema migrations on a live database, or you want horizontal scaling (Vitess) without managing it yourself.
  • Pick Neon for Postgres with similar serverless pricing model.
  • Pick Supabase for Postgres bundled with auth/storage.
  • Pick AWS RDS / Cloud SQL if you want full control over MySQL configuration (different my.cnf settings, custom plugins, etc.).

PlanetScale removed its free tier in April 2024; pricing now starts at $39/mo. Real reason to pick PlanetScale today is the team-scale-ready architecture, not the bargain.

Sign up + create a database

1

Sign up at planetscale.com ↗. Pick a plan (no free tier; PlanetScale pricing ↗).

Create database:

  • Name — anything.
  • Region — closest to your app server. AWS, GCP, or PlanetScale-managed regions.
  • Cluster size — start with PS-10; scale up if needed.

Provisioning takes ~1–2 minutes.

Get the connection string

2

Database dashboard → Connect. Pick your language; PlanetScale generates the connection string with the right format.

DATABASE_URL=mysql://<user>:<password>@<host>/<db>?ssl={"rejectUnauthorized":true}

SSL is required (no plaintext connections). Most MySQL clients handle this automatically with the right URL.

Save to env. Credentials are scoped per-branch (more on that below) and can be rotated from the dashboard.

Connect from your app

3

Any MySQL library works. Common patterns:

Node + mysql2:

npm install mysql2
import mysql from "mysql2/promise";

const pool = mysql.createPool({ uri: process.env.DATABASE_URL });

const [rows] = await pool.query("SELECT * FROM users WHERE id = ?", [123]);

Serverless / edge (PlanetScale's HTTP driver):

npm install @planetscale/database
import { connect } from "@planetscale/database";

const conn = connect({ url: process.env.DATABASE_URL });

const results = await conn.execute("SELECT * FROM users WHERE id = ?", [123]);

The HTTP driver works in Cloudflare Workers, Vercel Edge Functions, AWS Lambda — places traditional TCP MySQL clients can't run. Serverless driver docs ↗.

Create your first table

4

PlanetScale Console → Console (Beta) tab → SQL editor. Or use any MySQL client (e.g., mysql CLI, TablePlus, DBeaver) against the connection string.

CREATE TABLE users (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) NOT NULL UNIQUE,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

ORMs: Drizzle, Prisma, Kysely all have MySQL adapters that work transparently with PlanetScale.

Branches + deploy requests (the killer feature)

5

PlanetScale invented database branching. The model:

  • Main branch — production. Cannot run DDL (schema changes) directly. Marked "production" so you can't drop a table by accident.
  • Dev branch — copy of main's schema. Run any DDL you want. Test the schema change in isolation.
  • Deploy request — when ready, open a "deploy request" from dev branch to main. PlanetScale shows the schema diff, runs a CI check on it, and applies the change to main using Vitess online schema changes (no table lock, no downtime).

Practical workflow:

pscale branch create my-db add-users-table
pscale deploy-request create my-db add-users-table
# Review in the dashboard, then:
pscale deploy-request deploy my-db <number>

Branching docs ↗ · Deploy requests ↗.

Install the CLI

6
brew install planetscale/tap/pscale   # macOS
# Or download from https://github.com/planetscale/cli/releases for Windows/Linux

pscale auth login

The CLI lets you connect to PlanetScale through a local proxy:

pscale connect my-db main --port 3306

Now your local mysql -h 127.0.0.1 connects to production (read-only by default; toggle to allow writes). Great for ad-hoc queries without exposing credentials in your terminal history.

No foreign keys by default

7

This is the most surprising PlanetScale gotcha: foreign key constraints are disabled by default because Vitess shards across nodes where cross-shard FK constraints don't work.

You can enable FK constraints for non-sharded databases via dashboard → Settings → Foreign key constraints. But the official recommendation is to enforce relational integrity in your application code (or via the ORM) instead.

If foreign keys are a hard requirement for you, Neon is friendlier.

Monitoring + insights

8

Dashboard → Insights. Shows:

  • Slowest queries by total time
  • Queries by execution count
  • Read/write split
  • Connection counts

Each slow query is clickable — see the exact text, average and p99 latency, suggested indexes (if applicable). Much friendlier than digging through MySQL's slow query log manually.

Backups

9

Automatic daily backups, retained per plan. Manual on-demand backups via dashboard or CLI:

pscale backup create my-db main

Restore is to a new branch (not in-place over main), so you can validate before swapping.

Vitess limitations to know. No native cross-shard JOINs (queries need to be written carefully if you eventually shard). No stored procedures or triggers. No CREATE INDEX CONCURRENTLY — but online schema changes handle that automatically. For most app code, you won't hit these; for power-user MySQL patterns, check the compatibility matrix ↗.

Pricing reality

10

Hobby plan ($39/mo) — 10 GB storage, 1 billion row reads/month, 10M row writes. Scaler plan ($59/mo) — 100 GB, scaler-pro for more.

Annoying gap: no free tier. If you're just starting out and need free, Supabase or Neon are better fits until you have revenue.

Official references

What's next