The Dev Log › Technology
PostgreSQL vs MySQL in 2026: Which Should Your Web App Use?
By Jezer Niel Blanca, Full Stack Developer ·
·
6 min read
Both are excellent for Laravel apps. The differences show up in JSON, indexing, migrations, case sensitivity, vectors and hosting. Here is how I choose between them.
"Should we use PostgreSQL or MySQL?" is one of the first questions on almost every new project I start. The honest answer is that both are excellent, battle-tested databases, and Laravel supports them equally well for everyday work. The differences show up at the edges: JSON handling, indexing options, migrations, case sensitivity, extensions and hosting. In this post I'll walk through those differences from a web developer's point of view so you can make the choice deliberately instead of by habit.
The Short Version
If you want my rule of thumb before the details:
- Choose PostgreSQL when you expect heavy JSON querying, need advanced indexing, want vector search inside the same database, or value transactional schema changes.
- Choose MySQL when your hosting environment is built around it, your team already knows it deeply, or you are working with an existing ecosystem that assumes it.
- Either is fine for a typical CRUD-heavy web app with users, teams, orders and reports.
The best database for most web apps is the one your team can operate confidently at 2 AM. Features matter, but familiarity and good backups matter more.
Where Laravel Hides the Differences
Eloquent, the query builder and migrations cover the vast majority of what a web app does, and they generate the right SQL for each database. A migration like this works identically on both:
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('customer_id')->constrained()->cascadeOnDelete();
$table->string('status')->index();
$table->decimal('total', 10, 2);
$table->json('meta')->nullable();
$table->timestamps();
});
Queries on JSON columns are also portable at the basic level. Laravel translates the arrow syntax into the correct JSON functions for each engine:
Order::query()
->where('meta->channel', 'mobile')
->whereJsonContains('meta->tags', 'priority')
->get();
So if you stay within Eloquent and the query builder, switching engines later is realistic. The trouble starts when you rely on engine-specific features, which is often exactly why you would pick one over the other.
JSON and Indexing: PostgreSQL's Home Turf
PostgreSQL has two JSON types, and jsonb is the one you want. It stores JSON in a binary format that can be indexed with GIN indexes, which makes containment queries across large tables practical. On PostgreSQL, Laravel's json column type creates a json column, while jsonb() gives you the indexable variant:
$table->jsonb('attributes')->nullable();
Then you can add a GIN index with a raw statement in the migration:
CREATE INDEX products_attributes_gin ON products USING GIN (attributes);
Partial and Expression Indexes
PostgreSQL also supports partial indexes, which only index rows matching a condition. That is perfect for tables where most queries target a small active subset:
CREATE INDEX orders_pending_idx ON orders (created_at) WHERE status = 'pending';
MySQL 8 supports functional indexes on expressions and can index JSON values through generated columns or multi-valued indexes, so it isn't helpless here. It just takes a bit more setup, and partial indexes in the PostgreSQL sense aren't available.
Migrations and Transactional DDL
This difference bites teams in production more than any other. In PostgreSQL, most schema changes are transactional. If a migration creates a table, adds a column and then fails on the third step, the whole thing can roll back cleanly.
In MySQL, DDL statements like CREATE TABLE and ALTER TABLE cause an implicit commit. A migration that fails halfway leaves your schema half-changed, and you'll need to fix it by hand before running it again.
My practical advice regardless of engine:
- Keep each migration small and focused on one change.
- Test migrations against a copy of production data before deploying.
- On MySQL, be extra careful with migrations that do several things at once.
Large Table Changes
Both engines have improved online schema changes a lot, but adding indexes or altering columns on very large tables still needs care. On PostgreSQL, CREATE INDEX CONCURRENTLY avoids blocking writes, although it can't run inside a transaction. On MySQL 8, many ALTER TABLE operations support ALGORITHM=INPLACE or INSTANT. In both cases, read the documentation for your specific change before running it on a busy table.
Case Sensitivity and Other Surprises
Some differences don't show up until a user reports a bug:
- String comparisons. MySQL's default collations are case-insensitive, so
where('email', 'Niel@example.com') matches a lowercase email. PostgreSQL comparisons are case-sensitive by default.
- LIKE queries. In PostgreSQL,
LIKE is case-sensitive and ILIKE is not. Laravel's whereLike() helper accepts a caseSensitive argument so you can be explicit on both engines.
- Returning inserted rows. PostgreSQL supports
RETURNING on inserts and updates. MySQL doesn't, although Eloquent handles IDs for you either way.
- Strictness. PostgreSQL is stricter about types. Comparing a text column to an integer without a cast will fail rather than silently converting.
User::query()
->whereLike('name', '%niel%', caseSensitive: false)
->get();
My habit is to normalise data on the way in. I store emails lowercased with a mutator or in the form request, so lookups behave the same on either database.
Search, Vectors and Hosting
Both engines have built-in full-text search, and Laravel's whereFullText() works with each when you add a full-text index in the migration:
$table->fullText(['title', 'body']);
For modest search needs, that is often enough before you reach for a dedicated search service.
The pgvector Factor
If your roadmap includes AI features like semantic search or retrieval, PostgreSQL has a strong advantage through the pgvector extension. It lets you store embeddings and run similarity queries in the same database as the rest of your data, which removes a whole piece of infrastructure. MySQL can store embeddings as JSON or binary data, but you would typically compute similarity in application code or use a separate vector store.
Hosting and Operations
Technical features aside, the environment often decides for you:
- Shared and budget hosting usually centres on MySQL or MariaDB. PostgreSQL may not be offered at all.
- Managed cloud databases offer both, with automated backups and point-in-time recovery.
- Local development is easy either way. Laravel Herd, Sail and Docker all make running either engine simple.
Whatever you choose, the operational basics are the same: automated backups you have actually tested restoring, monitoring for slow queries, and a plan for upgrades.
Can You Switch Later?
You can, but it's a project, not a setting. Data types, case-sensitivity assumptions, raw queries and engine-specific indexes all need review. If you think you might switch, keep raw SQL to a minimum and cover your important queries with tests that run against the target engine.
Wrapping up
PostgreSQL and MySQL are both great choices for Laravel apps. PostgreSQL shines with jsonb, partial indexes, transactional migrations and pgvector. MySQL shines with ubiquitous hosting, familiarity and a huge ecosystem. Stay inside Eloquent where you can, normalise your data, and choose based on the features you genuinely need and the team that will run it. If you're planning a new product and want help picking and designing the right data layer, my team and I would love to build it with you.
Tags: PostgreSQL, MySQL, Databases, Laravel