Methodology. Numerical thresholds reference SQLite’s “When to use” documentation and PostgreSQL’s official docs. We currently run two production apps on SQLite (with WAL mode) and three on Postgres. How we research.

Contrarian take

SQLite is fine in production for more apps than founders realize

Pieter Levels has discussed running multi-million-dollar SaaS products on a single VPS with simple stacks for years. Tailscale shipped its first production database on SQLite-via-etcd, then later on a single Postgres node, and the engineering write-ups suggest both choices were correct at the time. The point isn’t that SQLite is always right. It’s that the default assumption — “real apps need Postgres” — gets a lot of solo founders to over-engineer too early.

SQLite’s official documentation gives a usable rule of thumb: SQLite handles applications with up to ~100k page views per day on cheap hardware, comfortably. Per the SQLite team, the database is “the most widely deployed and used database engine” on Earth. It is not a toy.

When SQLite is genuinely fine in production

Before you migrate, audit honestly: are you actually hitting a SQLite limitation, or are you migrating because the internet told you to? SQLite is a strong default for any app that fits all of these conditions:

  • Read-heavy workload. Reads scale almost arbitrarily on SQLite. The bottleneck is writes.
  • Single-server architecture. If your app runs on one box (which most solo SaaS apps do at first), SQLite is sitting on the same disk as the app — zero network round-trips for queries.
  • Write rate under roughly 100 writes per second. With WAL mode enabled, SQLite handles thousands of writes per second on commodity hardware. The 100/sec figure is a comfortable, no-tuning number.
  • You don’t need fine-grained user concurrency. SQLite serializes writes (one writer at a time). For a SaaS where concurrent writers are bounded by the number of customers (not 10x that), this is rarely a real issue.
  • Backup is a daily file copy. You can cp app.db backup-$(date).db while WAL is on. That’s the disaster-recovery story for early-stage SaaS.

If all five describe your app, stay on SQLite. Don’t migrate. The operational cost of running Postgres — even managed Postgres — is non-zero, and the latency cost of moving the database off the application server is always positive.

Five thresholds that mean you actually need Postgres

Here’s the inverse: signals that the SQLite default has stopped being the right call. Numbers are approximate; treat them as orders of magnitude rather than exact cutoffs.

01

Concurrent writers regularly exceed ~100/sec

SQLite serializes writes. At low write rates this is invisible. Past ~100/sec sustained, write contention shows up as lock-wait latency. If your monitoring shows writes blocking on each other, Postgres’s MVCC concurrency model is the right answer.

02

You need to run on more than one server

SQLite is a single-machine database. Tools like LiteFS and rqlite extend it to clusters, but they add operational complexity. The moment your app needs horizontal scaling for the database tier (not just the app tier), Postgres — especially managed offerings like Neon and Supabase — becomes the cleaner default. See our Supabase vs Neon comparison.

03

Full-text search beyond what FTS5 can do

SQLite’s FTS5 module is excellent for basic full-text search. It hits a ceiling when you need semantic search, vector similarity, fuzzy matching with custom analyzers, or multilingual ranking. Postgres with the pg_trgm and pgvector extensions covers everything from typo-tolerant search to embedding lookups in a single database.

04

Heavy JSON workloads with indexed queries

SQLite supports JSON via the JSON1 extension, but indexing on JSON fields is awkward. Postgres’s jsonb type with GIN indexes is the gold standard for storing and querying schemaless data alongside relational data. If half your tables are JSON columns you query by key, Postgres pays for itself.

05

You need separate read replicas

Read scaling on SQLite is via the same disk as writes. If you need geographically distributed read replicas (analytics dashboards, customer-facing leaderboards, public read APIs), Postgres’s replication model is the standard tool. Managed Postgres providers offer this with one click.

How to migrate when the time comes

If at least two thresholds apply, migrate. Don’t panic-migrate; the work is mechanical. Plan a half-day of focused engineering. Approximate steps:

  1. Dump SQLite to SQL. sqlite3 app.db .dump > dump.sql. This produces a portable SQL file with schema and data.
  2. Mass-import into Postgres. Postgres won’t accept the dump as-is — SQLite uses slightly different syntax. Tools like pgloader handle the conversion automatically; for small databases (<1GB) the SQLite dump can be converted by hand in 20 minutes.
  3. Rewrite Postgres-specific syntax. Common gotchas: AUTOINCREMENTSERIAL or BIGSERIAL; SQLite’s flexible typing → explicit Postgres types; strftime(…)to_char(…). Your ORM (Prisma, Drizzle, etc.) handles most of these if it was Postgres-aware in code. Our Prisma vs Drizzle writeup covers ORM choices.
  4. Switch the connection string. Update DATABASE_URL from file:./app.db to your Postgres URL. Run schema migrations against the new database. Smoke-test in staging. Cut over.

Cut over with a brief maintenance window: stop writes to SQLite, take a final dump, import to Postgres, switch the connection string, restart. For most solo SaaS apps under 100MB of data, this is a 15-minute downtime.

Cost and effort table

Stack choiceMonthly costSetup effortOperational tax
SQLite on a $6 VPS$6ZeroFile backup script
SQLite + Litestream replication$6 + S330 minTest restore quarterly
Postgres on Railway$5–15 usage1 hourConnection management
Postgres on Neon (free tier)$030 minCold starts on free tier
Postgres on Supabase$0–2515 minAuth+DB coupled
Postgres on AWS RDS$50+ minimum3 hoursVPC, backups, IAM

The right answer depends on your hosting decision. If you’re already on Vercel + Railway (see our Vercel vs Railway breakdown), Railway Postgres is the path of least resistance. If you want a generous free tier with cold starts, Neon. If you want auth bundled, Supabase — covered in our Supabase vs Firebase comparison.

The actual recommendation

Default to SQLite for any solo SaaS that runs on a single server, has a read-heavy load profile, and does fewer than ~100 writes per second. Migrate to Postgres the moment you hit two of the five thresholds. Don’t migrate before. The marginal complexity of running Postgres for an app that doesn’t need it is real engineering time you could spend on the product instead.

Get one SaaS build breakdown every week

The stack, prompts, pricing, and mistakes to avoid — for solo founders building with AI.