Resources · Platform Building Blocks

Databases

Pick a database that scales from your laptop to ten thousand users without a Friday-night migration. Here's how we choose.

Most MVPs do not have a database problem. They have a "I picked the wrong database three months ago" problem. The fix is to pick something boring, relational, and cheap, and only graduate to something exotic when a real workload demands it. Here's the rundown of the options worth considering in 2026.

Turso (libsql / SQLite)

Turso is hosted libsql, which is a fork of SQLite with replication, edge replicas, and an HTTP/WebSocket protocol that works from serverless functions. The free tier is generous: 9 GB total storage, 1 billion row reads per month, 25 million row writes, and up to 500 databases. Yes, 500 databases — they aim that at multi-tenant patterns.

The reason this site (and the Vibe MVP starter) defaults to libsql is that you get a real dev.db file on your laptop for free, your tests run against the same engine your production runs against, and deploys don't require a managed database to come up first. Cold starts on Turso edge replicas are in the low single-digit milliseconds because the database is literally a file co-located with your function.

// db/index.ts
import { drizzle } from "drizzle-orm/libsql"
import { createClient } from "@libsql/client"

const client = createClient({
  url: process.env.TURSO_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN,
})

export const db = drizzle(client)

The catch: SQLite is a single-writer database. If your MVP is going to have thousands of writes per second from a single tenant, you'll feel it. Most MVPs will never feel it.

Neon (Postgres)

Neon is serverless Postgres with branching. The free tier in 2026 includes 0.5 GB of storage and 191 compute hours per month, which is plenty for a development branch and a small production database. Paid plans start at $19/mo for the Launch tier.

Neon's killer feature is database branching: every preview deployment can have its own copy of production data, snapshotted in seconds without copying the bytes. If your MVP needs Postgres-specific features — JSONB indexing, full-text search with tsvector, PostGIS for geography, range types — Neon is the cheapest way to get them in a serverless-friendly shape.

The downside is the "compute suspended" cold start. After 5 minutes idle on the free tier, the next query waits ~500 ms while the compute spins back up. That's fine for an internal tool. It's noticeable for a user-facing MVP.

Supabase (Postgres + Auth + Storage)

Supabase is "I want one dashboard for everything." Free tier: 500 MB database, 1 GB file storage, 50,000 monthly active auth users, 5 GB bandwidth. Pro is $25/mo. You get Postgres, an auto-generated REST API, real-time subscriptions over WebSockets, an auth service, file storage with presigned URLs, and edge functions.

Use it when the bundling is the value. Use it cautiously when you only want one of those four products, because pulling Supabase Auth out later is harder than pulling Auth.js out, and Supabase Storage is fine but not a category leader.

RDS / Aurora

AWS's hosted Postgres or MySQL. Powerful, expensive, and overkill for an MVP. The minimum cost for a db.t4g.micro Postgres instance with multi-AZ failover is around $40/mo before you even add storage and bandwidth. You don't need this until you have customers paying you more than $40/mo.

The right time to move to RDS or Aurora is when you've outgrown a serverless tier (sustained writes, big working set, complex replication topology) AND you have someone who can read CloudWatch metrics. Until then, it's a tax.

PlanetScale

PlanetScale is hosted MySQL with Vitess underneath. It used to have the best free tier in the business. In 2024 they killed it, and the cheapest plan is now $39/mo for the Hobby tier (formerly "Scaler"). The branching workflow is excellent and the schema-change UX is genuinely best-in-class.

We don't reach for PlanetScale on MVPs anymore because the price floor is higher than Neon, Turso, or Supabase, and the only thing it offers that those don't is MySQL itself. If you have MySQL skills and want a managed home for them, fine. Otherwise skip.

Drizzle ORM

Drizzle is a TypeScript-first ORM that compiles to plain SQL. It's a thin layer: schema is defined in TypeScript, queries look like SQL, the generated types are exact, and it works on libsql, Postgres, MySQL, and SQLite without surprise.

import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"

export const users = sqliteTable("users", {
  id: text("id").primaryKey(),
  email: text("email").notNull().unique(),
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
})

// Query
const all = await db.select().from(users).where(eq(users.email, x))

Migrations come from drizzle-kit generate, which diffs your schema and produces a SQL file you commit. Run drizzle-kit migrate in your deploy step. There is no shadow database, no migration engine running on your dev box, no surprise destructive operations. Compared to Prisma's migration flow this is a relief.

Drizzle's edge story is also better. It ships as plain JavaScript with no Rust binary, so it deploys cleanly to Cloudflare Workers, Vercel Edge, and Lambda without the size and cold-start penalty Prisma's query engine carries.

Prisma

Prisma is the more popular ORM, with more features, a graphical schema explorer, generated client code, and a more polished onboarding. The trade-off is that Prisma ships a Rust query engine binary that's around 18 MB compressed, which matters at the edge and in serverless. Prisma 5 introduced a "driver adapters" path that lets you skip the binary in some environments, but the rough edges are still there.

For a Node.js server on Render or Fly, Prisma is fine. For an MVP using Vercel Edge functions or Cloudflare Workers, Drizzle is the lighter, simpler choice.

Comparison

Database Free tier Latency profile ORM compatibility Branching
Turso (libsql) 9 GB, 1B reads/mo <5 ms (edge replicas) Drizzle, Prisma (via adapter) Yes (preview branches)
Neon Postgres 0.5 GB, 191 compute hr/mo 20-500 ms (cold start tax) Drizzle, Prisma, raw SQL Yes (best in class)
Supabase 500 MB, 50k MAU auth 30-100 ms Drizzle, Prisma, generated REST No native branching
AWS RDS None ($40+/mo floor) 1-10 ms in-VPC Anything No
PlanetScale None ($39/mo floor) 10-50 ms Drizzle, Prisma Yes (best UX)

Anti-patterns we keep seeing

Don't pick MongoDB for relational data. If your data has users that have orders that have line items, that's relational data. Document databases excel at "I have a bag of opaque JSON I retrieve by ID." Most MVPs don't have that shape, even when the founder is sure they do.

Don't pick AWS RDS for an MVP. The $40/mo floor is real, the IAM is the worst part of your day, and you're going to spend a Sunday afternoon learning what a Parameter Group is for no business reason.

Don't pick Firebase Realtime Database in 2026. Even Google has been gently steering people to Firestore for years, and Firestore itself has design constraints that bite at the moment you want to do a JOIN. If you must use Firebase, use Firestore and accept the constraints.

Don't put your DATABASE_URL in next.config.js or commit it to git. Put it in a .env file that's gitignored, and in your hosting provider's environment variables. Use a secrets manager when you have a team.

Don't let an AI agent run drizzle-kit push against production. Always go through generate + reviewed migration + migrate. The diff that push produces is sometimes destructive in ways that aren't obvious until the table is gone.

Our recommendation

For most MVPs in 2026, we use Drizzle ORM with libsql: a local SQLite file in development, Turso for production. Schema lives in TypeScript, migrations live in your repo as SQL files, and the dev loop is "edit schema, run drizzle-kit generate, restart server." It's free until you have real traction, and it scales further than most founders assume.

When the data calls for Postgres-specific features (JSONB queries, full-text search, geography, complex constraints), reach for Neon with the same Drizzle setup. Drizzle abstracts the dialect well enough that this is a 10-minute swap on day one and a real migration on day 100.

When you want auth, storage, and a Postgres database in one place and don't mind the lock-in, use Supabase. It's a fine answer; it's just not our default.