Migrating Prisma from SQLite to Postgres for Production

Written By
SprintX Team
AI & Product Engineering
July 11, 2026
9 min read

SQLite is perfect for local development and wrong for serverless production. Here is how to move a Prisma app to Postgres cleanly.
SQLite is a wonderful default when you are building with Prisma. It is a single file, needs zero setup, and makes local development instant. It is also the wrong database for most production apps — especially anything deployed to a serverless platform like Vercel, where there is no persistent disk to keep that file alive between requests.
If your app "works locally" but you are about to ship it to real users, moving from SQLite to Postgres is one of the highest-value things you can do. This guide walks through the migration end to end, including the parts the quick tutorials skip.
Why SQLite does not survive production
SQLite stores everything in a file on disk. That model quietly falls apart in production for a few concrete reasons:
- Serverless has no persistent disk. On Vercel and similar platforms, each function invocation may run on a fresh machine. Your SQLite file either does not persist or is not shared, so data silently vanishes or diverges.
- Concurrency is limited. SQLite locks the whole database on writes. A handful of simultaneous users is fine; a real workload is not.
- No managed backups, replication, or scaling. Postgres gives you all three; a file gives you none.
Postgres fixes every one of these. It is a real client-server database built for concurrent access, and every serious host offers a managed version.

Before you start: back up and branch
Do not run a migration against your only copy of the data. Back up your SQLite file, commit your current schema, and do the work on a branch. Prisma migrations are mostly reversible in theory and painful to unwind in practice — a clean fallback saves you.
Step 1: Provision a Postgres database
Pick a managed Postgres provider. For most apps we reach for Supabase, Neon, or Railway — all give you a connection string in minutes. Neon and Supabase are especially serverless-friendly because they include connection pooling, which you will need. Grab two connection strings if the provider offers them: a pooled URL for the app runtime and a direct URL for running migrations.
Step 2: Update the Prisma schema
In schema.prisma, change the datasource provider from sqlite to postgresql:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}
The directUrl line matters for serverless Postgres: Prisma uses the pooled DATABASE_URL at runtime and the unpooled DIRECT_URL when applying migrations, which need a direct connection.
Step 3: Watch for type differences
This is where the tutorials go quiet. SQLite is loosely typed and Postgres is strict, so some columns need attention:
| Concern | SQLite behavior | Postgres reality |
|---|---|---|
| Booleans | Stored as 0/1 integers | True boolean type |
| Dates/times | Often stored as text | Real timestamp types |
| Auto-increment IDs | Loose | Use serial/identity or a UUID default |
| Enums | Not enforced | Real enum types you can define |
| JSON | Stored as text | Native jsonb |
For a fresh production database this is mostly upside — you get stronger guarantees. Just review each model so Prisma generates the column types you actually want, and adjust defaults (for example, @default(uuid()) or @default(now())) explicitly.
Step 4: Create the schema in Postgres
With the provider changed, generate a fresh migration against the new database:
npx prisma migrate dev --name init-postgres
This creates the tables in Postgres. Run npx prisma generate to refresh the client. At this point you have an empty, correctly-typed Postgres schema.
Step 5: Move the data across
If your SQLite data matters, migrate it — do not just recreate the schema and lose it. For small datasets, the reliable approach is a short script that reads from the old SQLite database (via a second Prisma client) and writes into Postgres, table by table, respecting foreign-key order so parent rows exist before children. For larger datasets, export to CSV and use Postgres's COPY. Either way, verify row counts on both sides afterward and spot-check a few records.
Step 6: Add connection pooling
This is the step people skip and regret. Serverless functions open a new database connection on nearly every request and can exhaust Postgres's connection limit within minutes of real traffic. Use your provider's pooler — Supabase's pooler, Neon's pooled endpoint, or PgBouncer — as your runtime DATABASE_URL, and keep the direct URL only for migrations. Without this, your app works in testing and then throws "too many connections" the moment it gets popular.
Step 7: Test, then cut over
Run the full app against Postgres locally and in a preview deployment. Exercise the real flows, watch for the type differences above surfacing as bugs, and confirm dates and booleans read back correctly. Only then point production at the new database. If you are deploying this on Vercel, our Vercel production checklist covers the surrounding steps like environment scoping and domains.
Frequently asked questions
Can I keep using SQLite in production? For a tiny, single-instance app on a server with a persistent disk, occasionally. For anything serverless or with real concurrent users, no — you will lose data or hit write locks. Postgres is the safe default.
Will my Prisma queries change? Mostly no. Prisma abstracts the query layer, so your application code largely stays the same. The changes are in the schema provider, column types, and connection configuration.
Do I really need connection pooling? On serverless, yes, without exception. Functions open connections faster than Postgres can handle them. A pooler is the difference between an app that scales and one that crashes under its first traffic spike.
What is the safest way to move the data? Back up first, migrate table by table in foreign-key order, and verify row counts on both databases before cutting over. Keep the SQLite file until you are certain the new database is correct.
Staring at a Prisma app that only works because SQLite lives on your laptop? SprintX migrates apps to production Postgres — schema, data, connection pooling, and deployment — with no lost rows and no surprises under load. Fixed-scope quote, you own the result, no lock-in. Get in touch and we will get your database production-ready.


