If your app already runs on Postgres and you need "search that finds the right rows when someone types a few words," you almost certainly do not need Elasticsearch yet. Postgres has had built-in full-text search for over a decade — stemming, ranking, and a GIN index that keeps queries fast — and for most apps under a few million rows it is fast enough, accurate enough, and one fewer service to operate. Reach for a dedicated search engine when you hit its real limits, not by default.

I keep watching teams stand up an Elasticsearch or OpenSearch cluster for what is, in practice, a search box over ten thousand blog posts. Then they inherit an entire second data store to keep in sync, secure, back up, and pay for. Below is what Postgres actually gives you, how to wire it up, and the honest list of when it stops being enough.

What does Postgres full-text search actually do?

Postgres full-text search converts text into a tsvector — a sorted list of normalized lexemes (word roots) with their positions — and matches it against a tsquery. The normalization step is what makes it real search rather than LIKE '%word%': it lowercases, strips punctuation, removes stop words, and stems, so a search for running matches a document containing run, and mice can match mouse depending on the dictionary.