MySQL vs Postgres vs SQLite

Use Postgres by default for its features and extensions; use SQLite when the database can live next to the app; use MySQL if you already run it well.

Editorial judgement last reviewed

Which database should I use: SQLite, Postgres or MySQL?

  • This page deliberately does not rank these; a scoreboard would be dishonest.
  • Three good engines with different deployment models.
  • PostgresPostgreSQL logo is the default answer, and it should be.
  • Strictest standards compliance, and the best type system: arrays, JSONB, ranges, native UUIDs, custom types.
  • An extension ecosystem no other database matches: PostGIS, pgvector, TimescaleDB, pg_cron.
  • Its MVCC gives proper snapshot isolation.
  • No reason to choose otherwise: choose PostgresPostgreSQL logo and stop reading.
  • SQLiteSQLite logo is the most underrated option in web development.
  • Not a toy: the most widely deployed engine in the world, exhaustively tested.
  • On modern hardware with WAL mode it handles far more concurrent reads than people expect.
  • In-process queries cost microseconds, so naive code can outrun careful batching against PostgresPostgreSQL logo.
  • The constraint: a single writer and a database on one machine's disk, which many apps never hit.
  • MySQLMySQL logo is the pragmatic incumbent.
  • Fast, well understood, with mature replication battle-tested at enormous scale and cheap managed hosting.
  • Its type handling has historically been laxer, and its extension story is thinner.
  • If you already run it well, there's no urgent reason to move.
  • The right question isn't which is best, but where your data should live: in-process, on a server, or managed.

Which one should you pick?

Choose Postgres if…

  • You want the default: safest for a new project and the most transferable skill.
  • You need extensions: PostGIS, pgvector, full-text search, time-series, in-database scheduled jobs.
  • Your data model wants real types: arrays, indexed JSONB, ranges, enums, custom domains.
  • You need strict correctness: transactional DDL, thorough constraint checking, no silent coercion.
  • You want the widest managed hosting: SupabaseSupabase logo, Neon, RDS, Hetzner and everything between.
  • You expect analytical queries alongside transactional ones, where the planner earns its keep.

Choose SQLite if…

  • The database can live next to the app: a single server, edge deployment, desktop, mobile or CLI.
  • Read traffic dominates writes, which describes most web applications honestly measured.
  • You want microsecond queries: in-process means no network, pool or serialisation.
  • You want zero operations: no server to patch, no connection limits, no failover to design.
  • You're building local-first or offline-capable software, where SQLiteSQLite logo is the only sane answer.
  • You're testing: SQLiteSQLite logo gives instant, disposable test databases, though watch dialect differences.

Choose MySQL if…

  • Your team already runs it well: operational familiarity beats a feature checklist.
  • Your platform or framework assumes it, as PHP, WordPress and legacy Java tooling do.
  • You need mature replication topologies and a deep bench of people who've operated them.
  • You want the cheapest managed hosting; the MySQLMySQL logo market is more commoditised.
  • You're using a MySQLMySQL logo-compatible distributed engine (PlanetScale, Vitess, TiDB) for horizontal scaling.
  • You're migrating an existing MySQLMySQL logo app and migration cost outweighs the feature difference.

Concurrency: the real difference between the three

  • Everything else here is detail; the concurrency model is the actual decision.
  • SQLiteSQLite logo allows one writer at a time to the database file.
  • In WAL mode readers don't block the writer and vice versa, removing the historical objection for read-heavy work.
  • But two simultaneous writes serialise, and a long write transaction makes the second wait.
  • SQLiteSQLite logo is excellent up to hundreds of writes per second, unsuitable for sustained heavy concurrent writes.
  • PostgresPostgreSQL logo uses MVCC with process-per-connection: many writers proceed, each seeing a consistent snapshot.
  • Connections are expensive, hence PgBouncer or a built-in pooler on nearly every serious deployment.
  • Long-running transactions block vacuum, the source of most PostgresPostgreSQL logo operational pain.
  • MySQLMySQL logo with InnoDB is also MVCC, with thread-per-connection handling large connection counts gracefully.
  • A genuine advantage with thousands of connections and no pooler in the path.
  • Estimate peak concurrent writes honestly, not aspirationally.
  • Most apps are far below where this matters, and complexity gets bought for scale that never arrives.

SQLite in production, and the objections that no longer hold

  • Two things changed the calculus.
  • Modern servers are absurdly fast, and WAL mode plus sensible pragmas removed most of the concurrency objection.
  • One NVMe machine running SQLiteSQLite logo serves what a 2010 engineer would have insisted needed a cluster.
  • The tooling caught up too.
  • Litestream streams the WAL to object storage, giving point-in-time recovery for the price of an S3 bucket.
  • LiteFS and libSQL/Turso offer replication and edge distribution.
  • Several frameworks now ship SQLiteSQLite logo as a first-class production target.
  • What you give up: horizontal write scaling, since there's one writer and one file.
  • Running the database on a different machine from the app, which some architectures require.
  • Concurrent connections from many app servers, and a rich type system.
  • SQLiteSQLite logo's dynamic typing is permissive; STRICT tables are a recent mitigation.
  • What you gain: no network latency, pool tuning or failover design.
  • Backups are a file copy, and dev is identical to production.
  • If you deploy to one machine with writes in the thousands per second, SQLiteSQLite logo probably beats managed PostgresPostgreSQL logo over a network.
  • Consider it seriously before dismissing it.

Types, JSON and correctness

  • PostgresPostgreSQL logo has the strictest type system and refuses operations the others silently permit.
  • Native arrays, GIN-indexed JSONB with rich operators, ranges, proper enums, first-class UUID, custom domains.
  • If a bad row is worse than a failed insert, PostgresPostgreSQL logo pays you back.
  • MySQLMySQL logo has narrowed the gap: real JSON, strict SQL mode by default, generated columns.
  • Older deployments carry habits from silent truncation and zero dates; the engine moved on, legacy schemas often haven't.
  • SQLiteSQLite logo uses dynamic typing: an INTEGER column accepts a string unless the table is STRICT.
  • Declare STRICT tables on anything new.
  • It lacks native boolean and date types, storing them as integers and text.
  • Every ORM handles that, but it surfaces in raw queries.
  • For JSON-heavy schemas PostgresPostgreSQL logo is clearly ahead: binary JSONB, good indexing and operators.
  • MySQLMySQL logo's JSON is competent; SQLiteSQLite logo's json1 is fine for occasional use, not a document-store substitute.

Operations, hosting and the cost of running each

  • SQLiteSQLite logo has no operations in the usual sense: no server, user management, connection limits or failover.
  • Backup is a file copy or Litestream to object storage; scaling is a bigger machine.
  • A legitimate strategy up to a surprisingly high ceiling.
  • PostgresPostgreSQL logo requires more care than its reputation suggests.
  • Autovacuum tuning matters, long transactions cause bloat, pooling is mandatory above small scale.
  • Major version upgrades need planning.
  • Managed providers absorb most of this: SupabaseSupabase logo, Neon, RDS, Cloud SQL and a dozen others, at reasonable prices.
  • MySQLMySQL logo's operational story is the most commoditised.
  • Managed MySQLMySQL logo is cheap and everywhere, replication well trodden, failure modes documented over thirty years.
  • For the smallest chance of an undocumented problem, this is it.
  • Whichever engine you pick, test your restore and practise on a schedule.
  • The difference between a good and bad database decision is almost always whether the backup worked.

Frequently asked questions

Is SQLite really OK for a production web app?

  • Yes, for a large class of them.
  • Deploy to one machine, keep writes in the hundreds or low thousands per second.
  • Use WAL mode with Litestream for continuous backup.
  • SQLiteSQLite logo isn't a compromise: it removes network latency and an entire operational surface.

Should I move from MySQL to Postgres?

  • Only with a concrete reason: an extension you need, a type-system problem you keep hitting, or a hosting platform.
  • A migration is weeks of work plus a cutover risk.
  • Feature envy is not a reason.

Which is fastest?

  • It depends entirely on the workload.
  • SQLiteSQLite logo wins on simple reads: no network hop.
  • PostgresPostgreSQL logo wins on complex analytical queries: the best planner of the three.
  • MySQLMySQL logo is very fast on simple indexed lookups at high connection counts.

What about MariaDB?

  • A MySQLMySQL logo fork that has diverged, with some features MySQLMySQL logo lacks and a different governance model.
  • If you're already on it, staying is fine.
  • For a new project, choose between the three here rather than adding a fourth variable.

Can I use SQLite with multiple application servers?

  • Not against one shared file over a network filesystem, the classic way to corrupt a SQLiteSQLite logo database.
  • Workable patterns: one writer with read replicas (LiteFS).
  • Or a hosted libSQL service such as Turso that puts a protocol in front of the engine.

Which has the best vector search for AI features?

  • PostgresPostgreSQL logo with pgvector, comfortably.
  • Mature, indexes properly with HNSW, and filters on relational columns in the same query.
  • SQLiteSQLite logo has vector extensions that work at small scale.
  • MySQLMySQL logo's story is the weakest of the three.