An architecture plan, drawn up before there was a single user, carried this list: PostgreSQL for durable data, Elasticsearch for search, Redis for cache, MongoDB for flexible documents, RabbitMQ for queueing. Five separate data systems — each with its own backups, monitoring, version upgrades, and distinct failure mode.
Most of that list, for a long time, a single PostgreSQL carries on its own. The question isn’t “can PostgreSQL do this”; it’s “did you really buy the operational burden of these five systems”.
PostgreSQL’s little-known breadth
Treating PostgreSQL as just a table-row store uses a small fraction of what it can do. Most of the needs that prompt a separate system are already inside it:
- Full-text search. A real search built on
tsvector,tsquery, and a GIN index. It covers most apps’ “search products” and “search posts” needs, including stemming, weighting, and ranking. - Document storage. A
jsonbcolumn, paired with a GIN index, is a queryable document store. You can keep the fields that want schema flexibility right inside the relational table. - Queues.
SELECT ... FOR UPDATE SKIP LOCKEDandLISTEN/NOTIFYrun a moderate-volume job queue without a separate broker. Laravel’sdatabasequeue driver uses theSKIP LOCKEDhalf of it — its workers poll the jobs table at intervals rather than waiting on aLISTEN/NOTIFYsignal. - Analytical queries. Window functions, CTEs, materialized views — serious reporting without reaching for a separate analytical database.
- Geospatial data. Location queries via the PostGIS extension.
These features aren’t “present but unused”; they’re mature capabilities that run reliably in production.
The quiet payoff of a single system
Every new data system isn’t just a box; it’s a maintenance commitment. What you gain by staying on one system:
- One backup line. One backup/restore procedure, one recovery drill.
- One monitoring target. One set of metrics to learn, one alerting setup to build.
- Cross-consistency. This is the most important one. If your search index lives in the same database as your data, there can be no drift between them — both update in the same transaction. A separate Elasticsearch will eventually fall out of sync with the data, and fixing that becomes its own line item.
- One mental model. The team learns the quirks of one system, not five.
This is the data-layer version of the innovation-token logic from the boring architecture piece: every new system is a token, and tokens are limited.
Where the limit begins
PostgreSQL isn’t enough for everything — it has honest limits, and ignoring them is also a mistake:
- Search. When typo tolerance, advanced relevance tuning, faceted search, and multilingual analysis are needed at serious scale, a dedicated search engine genuinely pays off.
- Queues. When you need very high throughput, complex routing, or multi-consumer fan-out, a real message broker is the right tool. A PostgreSQL queue is comfortable at moderate volume, not at the extremes.
- Write throughput. Sustained writes beyond a single primary’s fsync capacity — this is the hardest breaking point in data-intensive systems, and a real limit.
When you hit one of these limits, bringing in an extra system is the right call. Bringing it in before you hit it is just a guess.
The decision: PostgreSQL first, then measure
The rule is plain: in a new project, make PostgreSQL do everything PostgreSQL can do. Let it be the first stop for search, queue, and cache-like needs. A separate system should arrive only when a measured limit is crossed — not from a hunch, a blog post, or résumé anxiety.
Most teams use maybe 20% of PostgreSQL, then say “it’s not enough”. What’s not enough is usually not PostgreSQL, but the effort to get to know it.
PostgreSQL isn’t enough for everything — but it’s enough for far more than you think. Before you bring in a second data system, ask whether you’ve truly reached the end of the first.
PostgreSQL first; let the rest follow from measurement.

Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.