I’ve reached for Elasticsearch more than once without really thinking about it. You need search, ES is what you reach for. It’s the reflex.
Here’s what the reflex skips, and it’s the thing that matters most when you’re small. Elasticsearch isn’t a feature you add. It’s a system you run. Another database sitting next to the one you already have, kept in sync, one more thing to monitor, back up, and get paged for. At 3am, alone, because you are the whole ops team.
That cost has nothing to do with speed. For a solo founder or a small team, the question was never “is Elasticsearch faster.” It’s “do I want to operate a search cluster to find out.” Usually the honest answer is no.
Because Postgres already searches, and for the size most of us actually run, it isn’t a compromise.
ALTER TABLE docs ADD COLUMN search tsvector
GENERATED ALWAYS AS (
to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,''))
) STORED;
CREATE INDEX docs_search_idx ON docs USING GIN (search);
SELECT id, ts_rank(search, q) AS rank
FROM docs, websearch_to_tsquery('english', $1) q
WHERE search @@ q
ORDER BY rank DESC LIMIT 20;
No new service. No sync. The index updates in the same transaction as the write, so stale search results aren’t a bug you can have. It’s a column and an index on the database you already run.
Does skipping the cluster cost you speed? I measured it, and put the whole benchmark online. A million real product reviews, Postgres and Elasticsearch side by side. This isn’t a lab-grade benchmark and doesn’t try to be. It’s numbers from a Mac Mini M4 running Docker, meant to show roughly how search slows down as the data grows, not to give you exact figures:
| rows | typical | worst case |
|---|---|---|
| 10,000 | 0.1 ms | 1 ms |
| 100,000 | 0.8 ms | 12 ms |
| 1,000,000 | 15 ms | 143 ms |
Read the top two rows, because that’s the size most apps actually are. At a hundred thousand documents Postgres answers a typical query in under a millisecond. No human can tell that apart from a dedicated cluster. You would run a second system to save time nobody can notice.
The bottom row is real, though. At a million rows the worst queries cross 100 ms. But what makes a query slow isn’t the size of the table, it’s how many rows it matches. Your everyday searches stay instant even at a million rows. Only broad common-term queries over a large dataset cost you.
In practice that ties back to dataset size, and other benchmarks put the working range clearly. Xata recommends starting with Postgres-only search for tables under 100,000 rows. Neon, testing at ten million, found Postgres’s built-in search had gotten too slow by then, while a stronger search index kept up. So the range is roughly this: the built-in search is comfortable into the hundreds of thousands, and once you’re into the millions it’s worth adding something stronger.
You have two options inside Postgres, and you reach for them in order. First, the built-in full-text search, the code above. Nothing to install, it ships with every Postgres, and for a small-to-medium dataset it’s all you need. The costs are mild: it adds some storage, and its ranking is basic, so it won’t give you finely tuned relevance. Second, pg_search, an extension that adds the same kind of ranking Elasticsearch uses, directly to Postgres. In Neon’s test it matched or beat Elasticsearch and handled a single database up to hundreds of millions of rows. The catch is it’s a third-party extension, so you need a Postgres you can install it on.
So here’s my opinion, plainly. Solo and small-to-mid teams over-engineer search constantly, and it’s almost never about load. It’s fear, or habit, or wanting the resume line. We reach for the cluster before we have the problem the cluster solves.
Match the tool to your scale. Start with the search that’s built into every Postgres. Into the millions, pg_search gives you stronger ranking without leaving the database. Elasticsearch comes last, for when you outgrow a single machine: billions of rows, search load you need off your main database, or scale you have to spread across several servers. Those are real reasons, and you’ll know when they’re yours.
Until then you’re not running Google. You’re running a search box over a dataset that fits in your database. Add one index. Don’t run big-company infrastructure until you have big-company problems.