I would default to Postgres. Here’s the only time I still pick MySQL

James Okonkwo

James Okonkwo

August 28, 2026

I would default to Postgres. Here’s the only time I still pick MySQL

I default to Postgres. I say that in kickoffs and I mean it. The only time I still pick MySQL is when MySQL is already the system we can restore, staff, and explain at 3 a.m. Not when a blog says it is faster. Not when a host has a pretty branching demo. When the estate is already MySQL and healthy. That is the whole article. The rest is how I tell “healthy” from “we are scared to touch it,” and how I refuse the fake exceptions.

The comparison I actually make lives in Postgres vs MySQL in 2026. This is the exception rule I will defend when someone wants a second engine for taste.

The only real exception: a live, staffed MySQL estate

If backups restore, on-call can read EXPLAIN in this dialect, the host is paid for, and the last incident was not “we do not know this database,” I will start the next service on MySQL. I will use the same patterns. I will not open a Postgres cluster so the architecture diagram looks modern. Two engines is a tax: two restore stories, two upgrade calendars, two ways to get a lock wrong. I pay that tax for a noun I can name. “I like JSONB” is not a noun.

I have broken this rule once. We added Postgres “just for the new app.” Six months later we had a sync job, a confused analytics person, and an on-call rotation that treated one engine as a foreign language. The new app was fine. The estate was worse. I migrated the new app back. That is an expensive way to learn that defaulting to Postgres is for greenfield, not for a second door in a house that already has a kitchen.

Inherited server rack as existing infrastructure in dusty light

What counts as healthy

A restore in the last quarter that someone on the team ran, not only the vendor. A slow-query habit. A migration path people are not terrified of. Monitoring that is not “CPU is high.” If those are missing, I still might stay on MySQL — because the fix is the habit, not a new logo. Switching engines to escape a sloppy estate is how you get two sloppy estates.

If healthy is missing and the team wants Postgres as a clean start, I will allow it only as a new bounded system with its own owner and no fantasy that we will “unify later this year.” Later this year is how the sync job is born. I would rather fix MySQL operations for a year. It is less romantic. It works.

Fake exceptions I no longer accept

“MySQL is better for reads.” Show me the query and the plan. I have made both engines look bad with one missing index. I have made both look fine with a boring schema. This is not an exception. This is a benchmark mood.

“Our host only makes MySQL easy.” Sometimes true. If the blessed host is MySQL and compliance is real, that is the estate exception in a costume. If the host is a preference, I will still default to Postgres on a host we can also bless. Host preference is not a dialect religion unless legal says it is.

“WordPress is MySQL, so we should be.” WordPress is a product we inherited. The API next to it can be the same engine to keep ops simple, which is the estate rule again. Or it can be Postgres if it is a separate system with a separate owner. “CMS therefore company” is not a rule I use.

“We might need Vitess.” Then we have a number. If we do not have a number, we have a fear. I will not pick MySQL on day one to pre-buy a sharding story we will not staff. I have seen that pre-buy. We staffed nothing and we still had MySQL, which would have been fine if we had picked it for the estate, not for a unicorn.

“The ORM is happier.” Configure the ORM. If it cannot speak both, I question the ORM before I question a default I can hire for.

Team reviewing a restore drill checklist at a conference table

The vendor-dump exception, which is still the estate

Sometimes a partner only gives you MySQL dumps, or a tool only speaks the MySQL protocol. That is a constraint. I will run MySQL for that island. I will not convert the whole company. Island plus owner. If the dump is how we bootstrap a new product with no other constraint, I will still ask whether we can load it once and move. Often we cannot. Then MySQL stays. That is not a default change. That is a fence.

When I will not pick MySQL even if someone is loud

Greenfield, no estate, no dump, no blessed host: Postgres. A loud engineer who “knows MySQL better” is a training plan, not a default. I have been that loud engineer. I learned Postgres. The company did not need a second religion.

A new system that will need PostGIS or a serious full-text or vectors in the same place we already distrust a second store: I will argue for Postgres even next to a MySQL estate, and I will put the two-engine tax on the RFC in dollars and pages. If we will not pay, we do not get the feature that way. We buy a specialized store or we cut the feature. I will not sneak Postgres in as a “small add.” Small adds grow sync jobs.

The conversation with the person who wants a clean start

They are usually right that the MySQL schema is a mess. They are usually wrong that Postgres will stay clean. Mess is a team habit. I offer a bounded cleanup on the engine we have: the five worst queries, the one restore drill, the one migration rule. If they still want a new engine, I ask who owns the old one after. If the answer is “we will deprecate,” I ask for a date. No date, no second cluster. I have lived in deprecate-without-a-date. It is two production databases and a shrug.

If they want Postgres because they want to learn it, I support that on a side system or a read replica experiment, not on the checkout. Learning is not an exception to the estate rule. I learned that as the person who wanted to learn.

What I tell myself when I am tempted

I still get tempted. A new feature looks like JSON and vectors and I can feel the slide forming. I write the two-engine tax in the ticket: who pages, who restores, who writes the sync if we need it. If I cannot name those people, I do not pick MySQL or Postgres. I pick “not yet.” Then I default to the estate if we have one, or to Postgres if we do not. The temptation passes. The tax would not have.

The only time I still pick MySQL is when picking anything else would create a second estate I will not fund. That sentence has saved me from cleverness more than any benchmark. I default to Postgres when there is no estate. I stay on MySQL when there is. I do not stay because I love it. I stay because I have already paid for it, and I will not pay twice for a mascot.

How I write it on the RFC

Default: Postgres. Exception: we are extending a MySQL estate that restores and that this rotation can explain. If neither, I am not picking a database yet. I am picking whether we have an estate. Founders skip that sentence. I do not. The only time I still pick MySQL is when I can point at the cluster, the last restore, and the person who will get the page, and none of those are theoretical.

I default to Postgres because I am tired of bake-offs and I like the extras in one place. I pick MySQL when leaving it would be the second project. I will not pick MySQL to feel web-core or Postgres to feel serious. I will pick the one that is already a system, or the one I use when there is no system yet. That is the only split I still respect. Everything else is a comments thread I have already had, and a week I will not buy again. If you only remember one line: MySQL is not my underdog pick. It is my “we already live here” pick. Postgres is my empty-lot pick. I do not build a second house on the same lot because the other floor plan is nicer. I have the invoices from when I did.

More articles for you