# Is PostgreSQL Enough for Everything?

> Before adding a separate search engine, queue, or document DB: how far PostgreSQL gets you on its own, and where its limit begins.

- Type: Essays
- Published: 2026-08-01
- Updated: 2026-09-06
- Tags: postgresql, database, architecture, opinion
- Reading time: 3 min read
- Source: https://muhammetsafak.com/engineering/essays/is-postgresql-enough-for-everything/
- Language: en-US
- Author: Muhammet Şafak

---
> **TL;DR:** Most of the list that puts Elasticsearch, MongoDB and RabbitMQ next to PostgreSQL is carried by a single PostgreSQL for a long time: tsvector with a GIN index for search, jsonb for documents, SELECT FOR UPDATE SKIP LOCKED for a moderate-volume queue. The limits are honest and real, but a second data system should arrive on a measured limit, not on a hunch or a blog post.

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 `jsonb` column, 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 LOCKED` and `LISTEN/NOTIFY` run a moderate-volume job queue without a separate broker. Laravel's `database` queue driver uses the `SKIP LOCKED` half of it — its workers poll [the jobs table](/engineering/notes/sizing-a-postgres-queue-table-by-its-live-set/) at intervals rather than waiting on a `LISTEN/NOTIFY` signal.
- **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](/engineering/essays/why-boring-architecture/) piece: every new system is a token, and tokens are limited.

## Where does the limit begin?

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](/engineering/systems/data-intensive-systems-breaking-points/), 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.

## Sources

- [Full Text Search: tsvector, tsquery, stemming and ranking](https://www.postgresql.org/docs/current/textsearch-intro.html) — PostgreSQL
- [JSON Types: jsonb and GIN indexing](https://www.postgresql.org/docs/current/datatype-json.html) — PostgreSQL
- [SELECT: the locking clause and SKIP LOCKED](https://www.postgresql.org/docs/current/sql-select.html) — PostgreSQL
- [NOTIFY](https://www.postgresql.org/docs/current/sql-notify.html) — PostgreSQL

## Frequently asked

### Can PostgreSQL replace a separate search engine?

For most applications, yes. tsvector, tsquery and a GIN index give a real full-text search with stemming, weighting and ranking. A dedicated engine starts paying off when typo tolerance, advanced relevance tuning, faceted search and multilingual analysis are needed at serious scale.

### Can PostgreSQL be used as a job queue?

At moderate volume, yes. SELECT ... FOR UPDATE SKIP LOCKED lets several consumers pull work from a queue-like table without lock contention, and LISTEN/NOTIFY signals that the table changed. Very high throughput, complex routing or multi-consumer fan-out is where a real message broker is the right tool.
