~/comparisons/postgresql-vs-mysql-for-web-apps-6-questions-that-decide-it
§ POST · MAY 10, 2026 v1.0

PostgreSQL vs MySQL for web apps: 6 questions that decide it

PostgreSQL vs MySQL for web apps in 2026: the 6 questions that decide it (JSON, full-text search, replication, hosting), and where each one is the right pick.
Ryan CallowayStaff contributor
  9 min read

By Ryan Calloway. Updated May 2026.

“PostgreSQL or MySQL” is the most-asked database question on r/webdev for the third year running, and the answer that gets upvoted has not changed: PostgreSQL for greenfield projects unless you can name a specific reason to pick MySQL. The Stack Overflow 2025 Developer Survey backs the trend — PostgreSQL at 55.6% adoption among professional developers (up from 48.7% in 2024), MySQL at 40.5%, PostgreSQL “most admired” four years in a row at 65%. The more honest read lives in the recurring r/Database thread “PostgreSQL vs MySQL for a new project”, where six concrete cases still tip toward MySQL: PlanetScale and Vitess hosts, hosted CMS like WordPress, an existing team that runs MySQL, simple read-heavy workloads, multi-primary clusters, and tight binlog-driven CDC pipelines. Six questions decide everything else. This is that side-by-side.

Verdict at a glance

  • Best for: PostgreSQL 18 for new web apps — jsonb, partial indexes, pgvector, transactional DDL, virtual generated columns by default since September 25, 2025.
  • Not best for: PostgreSQL on PlanetScale, in a WordPress stack, or where your ops team already runs MySQL fleets at scale.
  • Watch out for: Postgres connection-pool footguns at scale (use PgBouncer or pgcat); MySQL JSON indexing requires generated columns; default isolation differs (PG: READ COMMITTED, MySQL: REPEATABLE READ).
  • Use MySQL 8.4 LTS for: read-heavy simple-CRUD workloads, hosted MySQL platforms (PlanetScale, Vitess), and teams with deep MySQL operational muscle memory.

Quick answer

PostgreSQL 18 by default for new web apps. jsonb with GIN indexes, generated columns, partial indexes, transactional DDL, and the catalog of extensions (PostGIS, pgvector, TimescaleDB) make it the safer long-term pick. Choose MySQL 8.4 LTS (or 9.x innovation) if you are deploying on a managed MySQL platform, your workload is read-heavy and write-light with simple queries, or your team already runs it cold. The six questions below tell you which bucket you are in; the schema-level differences explain why.

The short comparison

Concern PostgreSQL 18 MySQL 8.4 LTS / 9.x
Latest stable 18 (released September 25, 2025) 8.4 LTS (April 2024); 9.x innovation
Storage engine One engine (heap), MVCC built in InnoDB
JSON jsonb with GIN, full operator set JSON column; index via generated columns
Indexing B-tree, Hash, GIN, GiST, BRIN, partial, expression, B-tree skip scan B-tree, Hash, full-text, spatial
Generated columns Stored and virtual; virtual is the default in PG 18 Stored (indexable) and virtual (not indexable)
Vector search pgvector (de-facto standard, HNSW + IVFFlat) VECTOR type in 9.x; HeatWave for managed
Transactional DDL Full (BEGIN; ALTER TABLE; COMMIT) Single-statement atomic DDL since 8.0
Replication Streaming (WAL), logical (Pub/Sub) Logical (binlog), semi-sync, group replication
Default isolation READ COMMITTED REPEATABLE READ
Stack Overflow 2025 use 55.6% 40.5%

Six of those tilt toward PostgreSQL on a new project. The other four are roughly even, or tilt toward MySQL in narrow cases. The Bytebase team, who operate both at scale on Google Cloud SQL, summarise the picture in their 2026 PostgreSQL vs MySQL comparison: “for most workloads, performance is comparable within 30%; missing an index causes 10x to 1000x degradation regardless of which engine you chose.”

6 questions that decide it

1. How much of your data is JSON or semi-structured?

If user preferences, webhook payloads, audit logs, feature flags, or LLM tool responses are more than ~5% of your storage, this question alone settles the call. PostgreSQL’s jsonb stores binary JSON, supports indexing on individual keys with GIN, and exposes a rich operator set:

CREATE TABLE events (
  id bigserial PRIMARY KEY,
  payload jsonb NOT NULL
);

CREATE INDEX idx_events_payload ON events USING GIN (payload);

SELECT id FROM events
WHERE payload @> '{"type": "checkout", "country": "DE"}'
ORDER BY id DESC LIMIT 50;

One GIN index covers any key without changing the schema. MySQL’s JSON column type works, but you index it through generated columns:

CREATE TABLE events (
  id bigint AUTO_INCREMENT PRIMARY KEY,
  payload JSON NOT NULL,
  payload_type VARCHAR(50) AS (payload->>'$.type') STORED,
  INDEX idx_payload_type (payload_type)
);

It works. It is more verbose. You have to predict every key you might filter on. PostgreSQL’s GIN handles arbitrary access patterns with one index.

2. Do you need vector search?

RAG, semantic search, recommendation systems, LLM memory — pgvector is the standard answer in 2026. Install the pgvector extension, add a vector(1536) column, query with <=> for cosine distance. HNSW and IVFFlat indexes are mature. MySQL 9.x added a native VECTOR type with up to 16,383 dimensions, but indexing is still narrower; serious AI workloads on MySQL usually go through Oracle HeatWave, which is the enterprise managed offering.

3. How write-heavy and concurrent is your OLTP load?

This is the question where MySQL still has a real edge. Uber’s well-known “Why we switched from Postgres to MySQL” post is still the canonical case: under extreme write-amplification at fleet scale, PostgreSQL’s per-process connection model and MVCC bloat became operational pain. The OtterTune analysis walks through the same trade-offs (XID wraparound risk, autovacuum tuning) without the conclusion that you should migrate. For most web apps under, say, 5,000 writes per second, both are fine. Above that, MySQL’s InnoDB rollback segment is leaner than PostgreSQL’s heap MVCC if you are not careful with vacuum.

4. What does your replication and HA story look like?

PostgreSQL streaming replication via WAL is rock-solid for hot standbys. Logical replication (built in since PG 10) handles “replicate one schema to a different database” cleanly. PostgreSQL 18 added native uuidv7() for timestamp-ordered UUIDs that index well; see the PostgreSQL 18 release announcement (September 25, 2025) for the full feature list.

MySQL’s group replication is the more mature multi-primary story. If you need active-active writes across three nodes without a cluster manager bolted on, MySQL is the better-trodden path. PostgreSQL has BDR (commercial via EDB) and a few extensions, but the BSD-licensed core does not ship multi-primary. CDC pipelines also tilt slightly to MySQL: Debezium and similar consumers were binlog-native before they were WAL-native, and the binlog format remains less version-sensitive in production.

5. What’s your hosting and platform constraint?

This question often pre-decides the answer. Specialist hosts skew PostgreSQL: Neon’s Git-style branching for staging, Supabase’s full backend on PG, Crunchy Bridge’s operational chops. The best managed MySQL experience is PlanetScale’s Vitess-on-MySQL with branching, deploy requests, and online schema changes; if you are deploying there, MySQL is not optional. Hosted CMS and e-commerce platforms (WordPress, Magento, Shopify back-ends) are MySQL-shaped. Fighting that for a 5% feature gain is not worth the budget.

Provider PostgreSQL MySQL
AWS RDS, Aurora PostgreSQL RDS, Aurora MySQL
GCP Cloud SQL, AlloyDB Cloud SQL
Azure Flexible Server Flexible Server
Specialist hosts Neon, Supabase, Crunchy Bridge, Render PlanetScale, Vitess

6. How rigorous do you want the SQL surface to be?

PostgreSQL is more rigorous; MySQL is more forgiving. PostgreSQL is case-sensitive by default; MySQL is case-insensitive. PostgreSQL rejects non-aggregated columns in a SELECT with GROUP BY unless you list them; MySQL allows it. PostgreSQL supports both ROWS and RANGE window-frame types with the full set of range units; MySQL is narrower. Writable CTEs (UPDATE/INSERT/DELETE inside a WITH) are PostgreSQL-only. If you write a lot of analytical SQL, this gap shows up daily; if your queries are CRUD against a primary key, you will not notice.

The MVCC and isolation difference (less obvious, big in practice)

PostgreSQL’s MVCC keeps old row versions in the table heap until VACUUM reclaims them. MySQL’s InnoDB keeps old versions in the rollback segment. The practical effects:

If you have ever had a phantom-read or non-repeatable-read bug, the isolation default is why. Read the PostgreSQL isolation docs and the MySQL InnoDB isolation docs side by side once; the muscle memory pays back later.

Schema migrations: small but daily

PostgreSQL DDL is transactional. BEGIN; ALTER TABLE ...; COMMIT; rolls back cleanly on failure. MySQL 8.0+ has single-statement atomic DDL but most ALTER operations still take a metadata lock and rebuild the table; on a 300 GB users table, that is hours of locked writes. Tools like gh-ost exist precisely because online schema changes are not native to MySQL. PostgreSQL’s ALTER TABLE ADD COLUMN with no default is instantaneous; with a default it has been fast since PG 11. PostgreSQL 18 also retained pg_upgrade optimizer statistics, so the post-upgrade performance dip is much smaller in 2026 than it used to be.

The 6 cases where MySQL is still the right call

  1. You are deploying on PlanetScale or Vitess. The branching, schema migration, and sharding stack is best-in-class managed MySQL. There is no PostgreSQL equivalent at the same maturity.
  2. Your team already knows MySQL cold. The wins from PostgreSQL on a new project rarely outweigh re-training time and operational churn for an experienced six-engineer team.
  3. You are on a hosted CMS or e-commerce that requires it. WordPress, Magento, Shopify back-ends are MySQL-shaped.
  4. Heavy read workload, simple queries, no JSON. Both perform well; MySQL’s tooling for read replicas is slightly more turnkey out of the box.
  5. You need active-active multi-primary out of the box. Group replication is more battle-tested than the BSD-licensed PostgreSQL alternatives.
  6. Your data pipeline is binlog-native. Debezium-on-MySQL CDC pipelines that have been running for years are not worth migrating to PG logical replication for marginal feature gains.

Outside those six, PostgreSQL is the safer pick.

FAQ

Is PostgreSQL faster than MySQL in 2026?

Workload-dependent. PostgreSQL 18’s new asynchronous I/O subsystem reportedly gives up to 3x faster sequential scans, bitmap heap scans, and vacuum operations (per the PG 18 release notes). MySQL 8.4+ is faster on simple primary-key lookups at high concurrency. The Bytebase operating data: most workloads are within 30% either way; an unindexed query is 10x to 1000x slower regardless of engine. Run your real query mix on both before deciding on raw perf.

Should I migrate from MySQL to PostgreSQL?

Not unless you have a specific feature need (JSON indexing, pgvector, transactional DDL, partial indexes) that MySQL cannot meet. Migrations are 4 to 12 weeks of engineering work plus a year of operational learning curve. Greenfield: PostgreSQL. Existing healthy MySQL: stay unless there is a forcing function.

Does PostgreSQL really need VACUUM?

Yes. Autovacuum handles 95% of cases on default settings. Monitor pg_stat_user_tables.n_dead_tup; if it grows unboundedly, autovacuum is misconfigured. Lower autovacuum_vacuum_scale_factor on high-write tables. PostgreSQL 18’s I/O improvements made vacuum noticeably faster in practice; pre-18 tuning advice is still useful but slightly less urgent.

Which has better Django, Rails, and SQLAlchemy support?

Both have first-class drivers. Django’s QuerySet API has slightly more PostgreSQL-specific features (array fields, JSONField with operators, SearchVector); Rails has parity. SQLAlchemy supports both equally. Pick on database needs, not framework support.

What about MariaDB?

MariaDB started as a MySQL fork in 2009 and has diverged. For new projects, the choice is between PostgreSQL and either MySQL or MariaDB; between MySQL and MariaDB, pick the one your hosting provider supports as first-class. MariaDB and MySQL feature sets diverge enough that “MySQL-compatible” claims need testing on your actual queries.

Does MySQL 9.x change the calculus?

Marginally. MySQL 9.x added the VECTOR type, JavaScript stored procedures (Enterprise), and improved hash join performance. None of these close the gap on PostgreSQL’s jsonb indexing, generated-column flexibility, or extension ecosystem. If you were on MySQL anyway, 9.x innovation is a fine track for new features; 8.4 LTS is the safe production pick through 2032.

Sources and further reading

If you picked PostgreSQL and need to wire it into a backend framework, the FastAPI vs Flask vs Django REST guide is the next read. For the local dev setup that mirrors production, the Docker Compose tutorial walks through Node.js + Postgres + Redis end to end.

esc