PostgreSQL vs MongoDB: Pick the Workload Contract, Not the Logo
Pros-and-cons lists fail when Mongo needs `$lookup` for every report and Postgres JSONB rewrites the whole row on every sensor tick.
8 min read · May 27, 2026
#Programming #Databases #PostgreSQL #SoftwareArchitecture #MongoDB

You're about to run the comparison everyone runs: feature matrix, benchmark screenshot, declare a winner, move on. I've watched teams do exactly that — pick MongoDB for schema freedom, hit month three with normalized collections and join-shaped aggregation pipelines; pick Postgres for JSONB, then watch autovacuum and row rewrites eat a telemetry table alive under partial updates.
The databases aren't the problem. The workload contract is. Postgres optimizes for relational integrity, SQL analytics, and JSONB where the payload flexes inside a row you can still join. MongoDB optimizes for document-native storage, field-level patches, and horizontal sharding when a single primary can't absorb the write curve. Same word — "JSON" — different physics underneath.
Benchmark blogs optimize for clicks. Production optimizes for whether your hot path is a JOIN across three tables or a $set on one field in a two-megabyte document — and whether you'll still believe the brochure when maintenance windows spike tail latency.
The Comparison You Were About to Run — And Why It Lies
The usual post stacks rows: ACID yes/no, schema flexible yes/no, sharding built-in yes/no. Like you're buying a car from a checkbox list. That table treats databases like interchangeable engines with feature flags. They're not.
Postgres is a relational system that learned JSON (jsonb is decomposed binary storage with indexing — not a bolt-on text column). MongoDB is a document system that learned transactions and join operators when normalized models crept back in. Comparing them on "who has JSON" is like comparing nginx to a CDN because both cache bytes.
Speed claims without query shape are noise. Read-heavy catalog with GIN-indexed JSON paths? Postgres often wins on a single node. Sustained partial updates on large documents at high concurrency? MongoDB's own January 2026 evaluation shows steadier throughput and lower tail latency than JSONB on comparable cloud hardware — not because BSON is magic, but because row-based MVCC and field-level mutation are different contracts.
If your team already ships SQL reports and foreign keys, Postgres is the default conversation. If you're greenfield and your data is genuinely document-shaped with unpredictable fields at write time and you'll shard writes horizontally, MongoDB stays in the room. Everything else is a refactor wearing a comparison costume.
Two Storage Contracts — Rows You Can Join vs Documents You Can Shard
It's not "SQL vs NoSQL." It's what you optimize for when the schema misbehaves at 2 a.m.
PostgreSQL — SQL First, JSONB When the Payload Flexes
Relational modeling is still the spine: entities, constraints, migrations, EXPLAIN plans you can argue about in code review. When the product needs a settings blob, a CMS block, or an API payload that doesn't deserve its own table yet, jsonb holds it in binary form — faster to query than raw json text because the engine isn't reparsing on every read. GIN indexes turn containment queries into index lookups instead of sequential scans.
The contract: joins, window functions, reporting without exporting to a warehouse first. JSONB is the escape hatch, not the identity.
Most SaaS "we need Mongo for flexibility" stories die here. JSONB, migrations, Prisma/Drizzle — boring default, and Stack Overflow's 2025 survey still has Postgres on top of the admiration pile for databases.
MongoDB — Document First, Joins When You Gave Up on Embedding
MongoDB's modeling docs push embedding related data in one document when it stays bounded and updates together; reference when arrays grow without bound or get updated independently. Single-document writes are atomic — that's the happy path the manual wants you on.
The contract: shape data like your read and write paths, not like a third-normal-form ERD. When you normalize across collections anyway, $lookup is your join — it works, it's just not the idiomatic fast path. Teams that chose Mongo for "no schema" and then enforced relationships in application code traded database joins for GitHub issues.
The Partial-Update Contract — JSONB Row Rewrites vs BSON Field Patches
Both stores let you patch JSON. That API similarity hides the storage-engine gap.
MongoDB's update-heavy benchmark (256 concurrent clients, ~13M documents, 30-minute sustained load) reported MongoDB holding throughput steady while Postgres JSONB throughput declined over time — Postgres CPU pegged without translating to more completed updates. MongoDB attributes the gap to field-addressable $set on BSON versus Postgres creating new row versions under MVCC for JSONB updates — tuple rewrite and WAL pressure compound on hot rows.
Worked example: IoT fleet telemetry. Each device document is ~2 MB — nested diagnostics, firmware history, hundreds of sensor keys. Ingestion patches readings.temperature every second from 256 gateways. On MongoDB, $set targets the field; WiredTiger mutates what changed. On Postgres, jsonb_set on a telemetry row still versions the row — autovacuum chases dead tuples, indexes churn, p99 drifts upward while CPU looks "busy." For a read-heavy dashboard that fetches whole device profiles once per minute, Postgres with a GIN path on device_id is fine. For this write shape, you picked the wrong contract — not the wrong logo.
MongoDB published that benchmark. Treat it as evidence about update patterns, not as proof MongoDB wins your app.
Same API shape. Different row physics. Run your shapes: containment reads, nested filters, small patch updates under concurrency, then watch maintenance windows — autovacuum spikes on Postgres and compaction/checkpoint work on Mongo both show up in tail latency if you only measured day-one p50 (benchmark your actual JSON paths).
ACID on the Brochure vs ACID in Your Schema
Postgres transactions are the background radiation of backend engineering — BEGIN, COMMIT, multi-table invariants, rollbacks your ORM hides (SQL transaction basics). MongoDB multi-document ACID arrived in 4.0+ across replica sets and sharded clusters; drivers wrap sessions and retries (driver transaction guide).
The brochure says parity.
The manual says something sharper: distributed transactions cost more than single-document writes and should not replace effective schema design — denormalized documents remain optimal for many cases precisely because they minimize distributed transaction need (MongoDB transactions manual).
Worked example: checkout flow. Postgres shape: orders, order_lines, inventory — one transaction decrements stock and inserts lines with foreign keys enforcing referential integrity. Mongo shape that respects the contract: one orders document with embedded line items and inventory deltas applied atomically in a single-document update — no multi-collection transaction required. Mongo anti-pattern: three collections (orders, lines, inventory) updated under a multi-document transaction because the schema was normalized like SQL inside a document store — you bought Mongo's ops model and Postgres's modeling bill.
MongoDB has ACID. Postgres has ACID.
The design question is whether your schema needs cross-document atomicity often enough to pay the latency and ops tax — Mongo's docs argue you should engineer that answer toward "no."
Transactions on the brochure. Embedding in the schema.
Scaling — Vertical Postgres, Horizontal Mongo, and the Citus Middle
Postgres scales up first: bigger primary, read replicas for read fan-out, connection poolers when the app outgrows max_connections. When the write curve breaks a single primary, you graduate to extensions like Citus for distributed Postgres — real horizontal scale, more moving parts than "turn on sharding."
MongoDB ships sharding as a first-class cluster shape: shards, config servers, mongos routers — built for write scaling across nodes when the dataset outgrows one machine. The tradeoff is operating a distributed system earlier in your growth curve, and accepting that not every query pattern loves scatter-gather.
Neither is "more scalable" in the abstract. Postgres plus Citus is a project. Mongo sharding is a project.
Read replicas fix read fan-out; they don't fix write bottlenecks. Connection poolers fix too many clients; they don't fix hot rows. Pick based on whether you're likely to need shard keys and document locality, or whether read replicas plus partitioning will buy years.
Pros and Cons Without the Symmetry Theater
Comparison posts love symmetric bullet counts. Symmetry implies the tradeoffs are equal. They're not.
PostgreSQL — where it wins
- Default for new SaaS and B2B apps — SQL reporting, constraints, migrations, JSONB for the flexible corners
- Complex queries — joins, CTEs, window functions without aggregation-pipeline gymnastics
- Read-heavy JSON — GIN-indexed
jsonbpaths on a single node often match or beat document scans for catalog-style workloads - Integrity-first domains — finance, inventory ledgers, anything where referential integrity isn't negotiable
- Ecosystem gravity — managed Postgres (Neon, Supabase, RDS), ORM maturity, hiring and tooling depth reflected in developer surveys
PostgreSQL — where it hurts
- Hot partial updates on large JSONB rows — MVCC row versions and vacuum pressure under sustained field-level churn
- Horizontal write scaling — not as turnkey as Mongo sharding; Citus or architecture splits cost engineering time
- Schema migrations — discipline required; skipping them creates pain, not freedom
MongoDB — where it wins
- Document-native read/write paths — embedded data, one round trip, atomic single-document updates
- High-concurrency partial updates — field-level operators on large documents without rewriting the whole record
- Built-in horizontal sharding — when write volume outruns a single primary's runway
- Truly polymorphic documents at write time — rare, but real (form builders, telemetry with vendor-specific fields)
MongoDB — where it hurts
- Normalized relational shapes —
$lookuptax, app-enforced integrity, aggregation pipelines as SQL replacement - Distributed transactions as default — available, expensive, discouraged as a schema-design crutch
- Reporting and ad hoc analytics — often export to SQL-land or a warehouse sooner than Postgres teams do
Four Questions Before You Pick a Logo
Skip the feature matrix. Answer these with your actual routes and ops appetite.
- Query shape — Do you need multi-table joins, windowed reporting, and constraints on every write? Postgres. Is the hot path "fetch document by id, mutate nested fields, write back"? MongoDB — if the document stays bounded.
- Update pattern — Small patches on large records under high concurrency? Test JSONB row rewrite behavior before you assume Postgres is enough. Mostly reads with occasional full-document replace? Either may suffice; benchmark your path.
- Scale path — Will you hit single-primary write limits in the planning horizon? If yes, compare Mongo sharding ops to Citus or workload split — not "which logo is cooler."
- Schema discipline — Will the team maintain migrations and constraints, or will "schemaless" become undeclared schema in shared collections? Postgres punishes discipline lapses with migration pain; Mongo punishes them with production surprises. Pick the pain you will actually staff.
Honest scope: this isn't a migration guide, a replication deep dive, or an ORM shootout. It's the contract check before you commit.
PostgreSQL is the default for most new applications in 2026 — not because MongoDB lost, but because JSONB ate the easy flexibility arguments. MongoDB remains the specialized tool when document write patterns and horizontal scale genuinely match the contract.
What's the workload that forced your last database pick — and did the choice survive the first hot partial-update season?
More in Build
Your Cache Hit Rate Looked Fine Until the Hour Mark
Redis did its job on every miss — your application just sent two hundred loaders to Postgres at once.
6 min · June 15, 2026
PHP Turns 31 — The History That Matters Is the Elephant
The version timeline is everywhere. The resume logger, the Usenet post, and the sideways doodle that became a mascot — that's the birthday story worth telling.
6 min · June 10, 2026
BullMQ Background Jobs That Survive Production
Retries with an error taxonomy, deduplication that survives cleanup, and a dead-letter queue someone actually inspects — not a five-minute `Queue` demo.
6 min · June 6, 2026