Postgres or MySQL in 2026: the answer is almost always Postgres, but here's when it isn't
Three ecosystem shifts that clarified the decision, three workloads where MySQL still wins, and an honest answer to the migration question.
The Postgres vs MySQL debate should have ended years ago. It didn't, partly because MySQL is genuinely good, partly because most applications don't stress either system enough to notice the differences, and partly because the people asking the question are usually starting new projects where both feel like reasonable choices.
In 2026, the answer is clearer than it used to be. Not because MySQL got worse, but because the ecosystem around Postgres grew faster. For a new project, Postgres is the right default. The interesting question is whether your specific workload is one of the few where MySQL still wins.
What changed in the Postgres vs MySQL landscape since 2020
Three shifts made this comparison less interesting than it used to be.
Supabase became the default backend platform for a large cohort of new projects. Built on Postgres, it ships with pg_vector and PostGIS. Its growth moved tens of thousands of engineers toward Postgres who might otherwise have defaulted to MySQL through Laravel or the older PlanetScale offering.
PlanetScale dropped MySQL from its core offering in late 2024, pivoting to Postgres. PlanetScale built its brand on making MySQL sharding accessible. Its founders understood MySQL deeply. Walking away from it was not a casual decision.
Aurora Postgres also closed the performance gap with Aurora MySQL. For years, Aurora MySQL offered noticeably better throughput for read-heavy workloads. By 2025, Aurora v3 for Postgres had largely caught up, removing one of the more practical reasons to pick the MySQL variant of Aurora.
None of this makes MySQL broken. The ecosystem weight has shifted — and for most production workloads, ecosystem weight matters more than benchmark microseconds.
Where Postgres wins, and why the gap keeps growing
JSONB versus MySQL's JSON column
MySQL has JSON support. Postgres has JSONB. These are not the same thing. JSONB stores JSON as decomposed binary with native indexing: GIN indexes on JSONB columns can make queries against arbitrary JSON keys fast without materialising a separate column. MySQL's JSON column is parsed text with query functions layered on top.
For any application that stores structured-but-variable data (product attributes, configuration blobs, event payloads), the difference shows up in queries:
-- Postgres: GIN index works on any JSON field at query time
CREATE INDEX idx_events_payload ON events USING GIN (payload jsonb_path_ops);
SELECT * FROM events WHERE payload @> '{"type": "payment"}';
-- MySQL: requires a generated column per field you want to index
ALTER TABLE events
ADD COLUMN event_type VARCHAR(64)
AS (JSON_UNQUOTE(JSON_EXTRACT(payload, '$.type'))) STORED;
CREATE INDEX idx_event_type ON events (event_type);
-- You must know in advance which fields you'll query.The Postgres approach handles any JSON path at query time. The MySQL approach requires knowing ahead of time which fields need indexing. That difference compounds as schemas evolve.
The extension ecosystem
Postgres's extension architecture is its biggest structural advantage. pg_vector for embeddings, PostGIS for geospatial, pgcrypto for column-level encryption, pg_cron for scheduled jobs, pgaudit for compliance logging — these integrate with the query planner as first-class operations, not application-layer wrappers. MySQL has a plugin system, but the maintained ecosystem is narrower.
The AI angle matters in 2026: any application adding embedding-based search or similarity matching can do so with pg_vector inside Postgres without spinning up a separate Weaviate or Pinecone cluster. MySQL has no equivalent. For teams building AI features, this is a real decision point.
Logical replication
Postgres's built-in logical replication (mature since version 10) lets you replicate specific tables, handle schema changes during migrations without full restarts, and feed change data to Kafka via Debezium with clean semantics. MySQL's binlog-based replication works, but column-level filtering, GTID edge cases, and schema-change handling add operational complexity. The difference shows up in incident rates, not benchmarks.
| Dimension | Postgres | MySQL | Verdict |
|---|---|---|---|
| JSON/document storage | JSONB with GIN indexing and containment operators | JSON column (text + functions), generated column index required per field | Postgres |
| Extension ecosystem | 200+ maintained: pg_vector, PostGIS, pgcrypto, pg_cron, pgaudit | Plugin system, thinner coverage | Postgres |
| Logical replication | Native since v10, clean Debezium/CDC integration | Binlog-based, GTID complexity at scale | Postgres (slight) |
| Full-text search | tsvector/tsquery with ranking and dictionary support | InnoDB FTS, fewer configuration options | Postgres |
| Vector/AI workloads | pg_vector native, supported by all major managed hosts | No equivalent | Postgres |
| Read throughput (simple queries) | Excellent; occasional vacuum overhead on write-heavy tables | Slightly faster in high-read, low-write scenarios with tuned buffer pool | MySQL (narrow) |
| Multi-primary HA | Patroni + etcd or Citus (well-documented, multi-component) | Group Replication / Galera (simpler topology) | MySQL (slight) |
| Managed cloud options | RDS, Aurora, Cloud SQL, Supabase, Neon | RDS, Aurora (PlanetScale now Postgres-based) | Roughly equal |
| Ecosystem momentum | Growing: Supabase, Neon, pg_vector adoption accelerating | Stable, market share declining | Postgres |
MySQL's remaining strongholds
Most articles frame MySQL as purely legacy. That's not accurate. Three workload types still favour it.
Read-heavy workloads with simple query patterns
MySQL's InnoDB buffer pool, tuned for reads, can outperform Postgres on straightforward point-query and range-scan workloads. The MVCC implementations differ: Postgres keeps multiple row versions in-place, requiring vacuum to reclaim space and adding overhead on write-heavy tables. InnoDB uses a separate undo log that sidesteps this pattern. For high-read, low-write workloads like content platforms, catalogue lookups, and high-volume ID-based queries, MySQL can achieve slightly lower latency with less tuning.
Multi-primary high-availability setups
MySQL Group Replication and Galera offer synchronous multi-primary replication with a relatively simple operational model. Postgres's equivalent (Patroni with etcd or Consul) works well but involves more components to operate and monitor. Teams that have built operational expertise and runbooks around MySQL HA should not migrate just because Postgres is the default. The migration risk outweighs the theoretical advantage.
Existing codebases built around MySQL behaviour
MySQL and Postgres differ on several behaviours that are invisible until migration: case-insensitive string comparisons by default in MySQL (case-sensitive in Postgres), type coercion differences in strict mode, NULL handling in unique constraints, and GROUP BY semantics. A codebase with years of MySQL-specific workarounds is a migration measured in engineering-weeks — not because Postgres is harder, but because finding every implicit assumption is tedious work.
The migration question
If you're running MySQL and considering a switch, the honest answer is: usually not worth it unless you have a concrete pain point.
Migration makes sense when:
- You're repeatedly hitting JSONB's absence — materialising generated columns for every JSON field you query is creating real maintenance debt.
- You need pg_vector for an AI feature and don't want to operate a separate vector store.
- You're adopting Supabase, Neon, or another Postgres-native platform for new parts of your stack and want consistency across your data layer.
- Binlog replication edge cases are costing you incident hours per quarter.
Migration probably doesn't make sense when:
- Your application performs well and MySQL's operational simplicity has meant fewer incidents.
- Your team's MySQL expertise is an asset and the bottleneck is shipping features, not database behaviour.
- You're running a high-volume, simple-query workload where MySQL's tuning gives you a measurable edge.
Starting fresh in 2026
For a new project: start with Postgres. The extension library, JSONB advantage, ecosystem momentum, and the growing number of managed platforms all point the same direction. You'll hit the limits of MySQL's JSON handling before you hit the limits of Postgres's vacuum model.
For existing MySQL applications: staying is the right call if you're not hitting any of the pain points above. 'Postgres is better' is not an engineering reason to take on migration risk. A concrete blocker is.
The debate is largely settled. What matters now is knowing the three exceptions, and whether you're in one of them.
Frequently asked questions
Related reading
Zero-downtime Postgres schema migrations: what every DDL operation does under the hood
Most Postgres schema changes that cause outages aren't dangerous by nature — they're dangerous because the table is large. Here's what lock each DDL operation takes and the exact patterns to make them safe.
Three idempotency failure modes that only show up in production
Most idempotency guides stop at the happy path. Here are three failure modes — concurrent requests, partial commits, and request-body mismatch — with the Postgres patterns that fix each one.
Every Postgres isolation level, and the production bug it's designed to prevent
Most Postgres users never touch isolation levels — until a double-charge or an oversold booking forces the question. What each level allows, and the production bug that follows when you pick the wrong one.