Why I Picked PostgreSQL Over NoSQL for a SaaS Build

Six months ago, I started building a multi-tenant B2B SaaS with a client. We spent two weeks debating Postgres versus MongoDB, ran a prototype in both, and ended up on Postgres. This post walks through the assumptions that initially pushed us toward Mongo, why we reversed course, and the numbers that made the decision obvious in hindsight.
Disclaimer: this is a specific decision for a specific scale. If you are building a real-time social feed with hundreds of millions of documents, Mongo might still be the right call. For the 80% of B2B SaaS products that will never cross 10 million rows per tenant, Postgres wins.
Three assumptions that pushed us toward Mongo
- The schema was going to change constantly, and we wanted to avoid migrations. We assumed Mongo's schemaless model would let us iterate faster.
- The product was document-shaped — customer records with nested fields, audit trails, and config blobs. It felt natural to store those as documents.
- We expected hiring to be easier for Mongo than for Postgres. Every junior developer knows MongoDB from tutorials, right?
Why we reversed course
Each assumption fell apart when we looked at it honestly. Schemaless does not mean migration-free — it means your migrations become runtime checks scattered across your codebase, and your bugs become production incidents instead of deploy-time failures. Document-shaped data stores just fine in Postgres JSONB, with the advantage that you can also run real SQL against it. And hiring? Every backend developer I have worked with in the past five years prefers Postgres. The junior-developer-knows-Mongo story is a decade out of date.
Postgres vs MongoDB at our scale
| Axis | Postgres | MongoDB |
|---|---|---|
| ACID transactions | Full, default | Multi-document since 4.0, with caveats |
| Schema evolution | Migrations (predictable) | Runtime (unpredictable) |
| Analytics and reporting | SQL — anything is possible | Aggregation pipeline — limited |
| JSON storage | JSONB with GIN indexes | Native |
| Hosted pricing (mid-tier) | $50–150/mo on RDS or Supabase | $60–200/mo on Atlas |
| Hiring pool | Deep, broad | Broad, shallower at senior level |
| Joins | Native | Possible but painful |
JSONB is the unlock
The feature that made Postgres an obvious choice was JSONB. It lets you store arbitrary JSON documents in a column, index into specific paths, and query them with SQL. In practice, this means you get Mongo-like flexibility for the fields that actually need it — customer metadata, config blobs, audit payloads — while keeping strict schemas on the fields that benefit from them. Best of both worlds.
CREATE TABLE customers (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
email text NOT NULL,
metadata jsonb NOT NULL DEFAULT '{}'
);
CREATE INDEX customers_metadata_gin
ON customers USING gin (metadata);
-- Query into the JSON:
SELECT id, email
FROM customers
WHERE metadata->>'plan' = 'enterprise'
AND tenant_id = $1;That GIN index makes JSONB queries fast enough for real workloads. On the SaaS I was building, the customers table hit 2 million rows during load testing and metadata queries still returned in under 40ms. No sharding, no aggregation pipeline — just SQL.
“The best database is almost always the boring one your team already knows how to operate. Postgres in 2026 is the most boring database in the world, and that is exactly why you should use it.”
When I would still pick something else
- Document collections measured in hundreds of millions where write throughput dominates — Mongo or DynamoDB
- Heavy time-series workloads with rolling retention — TimescaleDB or ClickHouse
- Full-text search as the primary query pattern — Elasticsearch or Meilisearch alongside Postgres, not instead of it
- Edge-first applications with global latency requirements under 50ms — PlanetScale, Neon, or Turso
For anything that looks like a normal B2B SaaS, though, Postgres with JSONB is the default I now reach for. If you are in the middle of this decision for your own product and want to talk through the tradeoffs — get in touch. Related reading in my monolith vs microservices post, which covers the matching tradeoff on the service side.
Need help with your project?
Let’s talk about your technical requirements. I offer a free discovery call where we’ll discuss architecture, tech stack, and timeline.
View my services