How to Use ParadeDB: The Beginner's Guide (First Install, First BM25 Index, First Search Query, and the Keep-Elasticsearch-Anyway Rule)
CodexFuse Team · 2026-11-02T08:22:50.735Z · 16 min read min read
ParadeDB brings Elasticsearch-style search into Postgres via pg_search and pg_analytics. This beginner guide covers install, your first BM25 index, @@@ queries, relevance tuning, and when to keep Elasticsearch.
How to Use ParadeDB: The Beginner's Guide (First Install, First BM25 Index, First Search Query, and the Keep-Elasticsearch-Anyway Rule)
Learning how to use ParadeDB starts with one realization: the search engine your app needs may already be sitting inside your database. ParadeDB is a pair of Postgres extensions — pg_search for BM25 full-text search and pg_analytics for fast analytics — that bring Elasticsearch-style capability into the Postgres you already run, with zero new infrastructure and no data movement into another system. This beginner tutorial walks the whole arc: what ParadeDB is, the prerequisites, the install (Curlbash, Docker, or Postgres.app), enabling pg_search, creating your first ParadeDB BM25 index with the current 0.26 syntax, running your first @@@ search query, tuning relevance, the pg_analytics lane, and the honest keep-Elasticsearch-anyway rule for when a separate search cluster still wins. This is a ParadeDB tutorial written as a ParadeDB for beginners guide — the missing middle ground between vendor quickstarts and benchmark deep-dives. If you searched "Elasticsearch alternative Postgres" and landed here, you found the right page. If you have never run Postgres in a container before, our Docker beginner guide gets you oriented first. One framing line to carry through the whole guide: ParadeDB is search and analytics that stay inside Postgres — it does not replace your database, it upgrades it.
What Is ParadeDB, Exactly?
ParadeDB is an Elasticsearch alternative built on Postgres: two extensions, pg_search and pg_analytics, that act as drop-in solutions for full-text search and analytics inside Postgres itself. That is ParadeDB's own description, written by its team on star-history.com (April 18, 2024), and it is the sentence everything else in this guide hangs from. pg_search builds its search on Tantivy — the Rust answer to Lucene — making the ParadeDB pg_search extension a genuine BM25 engine, not just a wrapper around Postgres full text search. pg_analytics builds its analytics on Apache Arrow and DataFusion, the same columnar stack behind modern data warehouses. The whole project is written in 100 percent Rust and lives in the paradedb/paradedb repository on GitHub, which its README describes simply as "Postgres for Search and Analytics." The core extensions are AGPL-3.0 licensed, with a commercial enterprise add-on pack offered on top — a licensing line that matters if you plan to ship ParadeDB as a service. As of this writing (verified November 2, 2026), the project has around 8,883 stars (enterprisedna directory snapshot, June 2026) and the current pg_search release is 0.26.4, released October 29, 2026. The rest of this guide shows you what to do with it.
What Do You Need Before You Start?
You need a working Postgres environment and a terminal you are comfortable in. ParadeDB ships as the paradedb/paradedb Docker image, which the ParadeDB team says "includes everything you need to get started" — Docker tags on Docker Hub include 0.26.4-pg17 and 0.26.4-pg16, refreshed within days of this writing. But Docker is a choice, not a requirement: the official README publishes prebuilt extension binaries for Debian 12, Ubuntu 22.04 and 24.04, and Red Hat Enterprise Linux 8 and 9, covering Postgres 14 through 17, and states that ParadeDB supports every version the PostgreSQL Global Development Group supports — PostgreSQL 13 and up. If you are starting from a bare machine, our what is VPS hosting explainer covers the server side of that setup. You also need to know that pg_search 0.25 and later depends on pgvector — the vector extension must be present before pg_search will enable cleanly, a lesson that bites exactly once if you install by hand. The table below is your one-minute readiness check.
| Prerequisite | What "ready" looks like | Why it matters for ParadeDB |
|---|---|---|
| Postgres 14+ | A server you can psql into | Prebuilt binaries cover PG 14–17; Docker images ship PG 16/17 |
| pgvector | Extension installable | pg_search 0.25+ depends on it; enable with CASCADE |
| Docker (optional) | docker run works | Fastest route: the paradedb/paradedb image |
| shared_preload_libraries | pg_search listed + server restarted | Required for the Postgres.app and manual install routes |
How Do You Install ParadeDB?
You install ParadeDB through one of three routes, and you pick it once based on where your Postgres lives. The official install docs (paradedb.com, updated September 30, 2026) state it verbatim: "Get started locally with our Curlbash installer, Docker, or Postgres.app." The Curlbash installer starts ParadeDB in Docker with persistent storage, prints your connection credentials, and opens a psql session for you — it is the path of least resistance, and its one memorable instruction is to save the generated password so your application can connect later. The Docker route pulls the paradedb/paradedb image — tags like 0.26.4-pg17 were pushed days before this writing — and you connect with psql to localhost as the postgres user, the standard third-party-verified pattern. The Postgres.app route is the macOS lane: it assumes Postgres.app running PostgreSQL 18, you download the pg_search installer from the Postgres.app extensions page, find postgresql.conf with SHOW config_file, add pg_search to shared_preload_libraries, restart, and then — and this is the Postgres.app-specific grace note — "Postgres.app includes pgvector, which CASCADE enables automatically," so one CREATE EXTENSION ... CASCADE line finishes the job. Whichever route you take, say the choice lesson once: pick the route that matches the Postgres you already operate, because the goal is search inside your database, not a new database to adopt.
| Install route | Best for | The one thing to remember |
|---|---|---|
| Curlbash installer | Local first contact | Starts Docker with persistent storage; save the generated password |
| Docker image | CI, staging, production | paradedb/paradedb:0.26.4-pg17 (or -pg16); connect via psql on localhost |
| Postgres.app | macOS developers | pg_search in shared_preload_libraries; CASCADE pulls in pgvector automatically |
| Prebuilt binaries | Existing self-hosted Postgres | PG 14–17 on Debian/Ubuntu/RHEL; any supported Postgres 13+ |
How Do You Create Your First BM25 Index?
You create your first BM25 index with a plain CREATE INDEX statement — but you must write it in the current 0.26 syntax, because ParadeDB's index definition language changed under the hood. In ParadeDB 0.25 the access method was renamed from bm25 to paradedb; USING bm25 still works as a backwards-compatible alias, but the vendor's own 2026 blog posts show the new form, and new definitions should use USING paradedb. The verbatim current pattern, from ParadeDB's October 2025 hybrid-search blog, looks like this: CREATE EXTENSION pg_search IF NOT EXISTS, then CREATE TABLE, then CREATE INDEX idx_documents_bm25 ON documents USING paradedb (id, title::pdb.simple('stemmer=english'), content::pdb.simple('stemmer=english')) WITH (key_field=id). The pdb.simple('stemmer=english') casts are the tokenizer lesson: they tell ParadeDB how to split and stem each column's text at index time, and getting the stemmer right is half of relevance quality. Three rules sit around this statement, and each is worth saying once. First, enable with CREATE EXTENSION pg_search CASCADE so pgvector comes along — since 0.25 it is a hard dependency. Second, key_field is a deprecated no-op as of 0.26.0: Postgres will print "WARNING: key_field is deprecated as of 0.26.0 and is a no-op," so on current versions you simply omit it. Third, a table is capped at one pg_search index: attempting a second raises "ERROR: a relation may only have one ParadeDB index," so plan your indexed columns in a single definition. The older CALL paradedb.create_bm25(...) helper you may see in 2024-era writeups is superseded — plain CREATE INDEX is the current API.
| Syntax element | 0.25 and earlier | 0.26 (current) |
|---|---|---|
| Access method | USING bm25 | USING paradedb (bm25 remains a backwards-compatible alias) |
| Key field | WITH (key_field='id') required | Deprecated no-op; omit it (warning is harmless) |
| Index helper | CALL paradedb.create_bm25(...) | Plain CREATE INDEX ... USING paradedb |
| Indexes per table | One | Still one — "a relation may only have one ParadeDB index" |
How Do You Run Your First Search Query?
You run your first search query with the @@@ operator, which is ParadeDB's own query language — ParadeQL — and which is deliberately not Postgres's native @@ operator. ParadeDB says it chose the @@@ operator "in homage to the @@ operator used by Postgres' native full text search" (November 2023 announcement), and wherever possible it designed ParadeQL to mirror the Tantivy search-engine API transparently. In practice this means the right-hand side of @@@ accepts Tantivy's mini query language: field-targeted queries like description:keyboard^2 for boosting, boolean ORs, and fuzzy clauses like electronics:::fuzzy_fields=description&distance=2, straight from the vendor's own examples. The modern query pattern, confirmed in independent usage in October 2026, is column-first: SELECT id, title, paradedb.score(id) AS score FROM posts WHERE title @@@ 'index OR which OR to OR price OR an OR ssl OR transaction OR tcp OR api OR kernel' ORDER BY score DESC LIMIT 10. Two things to notice in that pattern. First, ranking comes from paradedb.score(key), which returns the BM25 score for each row so you can ORDER BY it — Postgres's native full-text search has no equivalent single step. Second, the syntax has evolved since 2023, when the docs showed my_table @@@ ('idx_mock_items', 'query'); the current form queries the column directly, and the old tuple form is a fossil you should not copy from old blog posts. The one section-level lesson: @@@ means "search the ParadeDB index"; @@ means "search Postgres's built-in tsvector" — they are different engines wearing similar punctuation.
How Do You Tune Relevance So the Right Results Win?
You tune relevance in three layers: the tokenizer, the scoring, and — for the ambitious — a retrieve-and-rerank pipeline, all inside the same Postgres. The tokenizer layer starts at index time with casts like pdb.simple('stemmer=english'), which control how text is split and stemmed; a wrong stemmer silently tanks recall on non-English content, so match it to your corpus language before you index a million rows. The scoring layer is BM25 with ParadeDB's own storage: pg_search 0.26.0 shipped a rewrite of how BM25 field-length data is stored for scoring — the fieldnorms moved from one shared array into each term's postings list. An independent October 2026 benchmark (dev.to, alexgeorgiev17) tested 0.26.0 against 0.25.11 on an identical 3-million-row table with a synthetic title column: a ten-term OR query dropped from about 129ms to about 29ms — roughly 4.3 times faster — but single-term queries regressed from about 1.4ms to 8ms, and pgbench at 10 clients showed single-term throughput dropping 5.6 times. The crossover sits around three to four OR'd terms, and the author states the honest caveat verbatim: "If your search boxes mostly take one or two words, this upgrade is a wash at best for you." For the ambitious layer, ParadeDB's own January 2026 personalized-search post demonstrates retrieve-and-rerank entirely in SQL: BM25 retrieval narrows to the top 100 candidates, then cosine similarity between each candidate's embedding and the user's taste vector re-ranks them, fused as 0.5 * normalized_bm25 + 0.5 * personal_score — "move the compute to the data, not the data to the compute." The benchmark table below keeps the vendor claims and the independent numbers in separate rows on purpose.
| Claim | Number | Who measured it |
|---|---|---|
| Ten-term OR query, 0.26.0 vs 0.25.11 | ~129ms down to ~29ms (about 4.3x faster) | Independent dev.to benchmark, Oct 2026 (synthetic 3M rows) |
| Single-term query, 0.26.0 vs 0.25.11 | ~1.4ms up to ~8ms (regression) | Same independent benchmark |
| Indexing vs Elasticsearch | 2.5x faster | ParadeDB's own benchmark (100M Wikipedia docs) — vendor numbers |
| Throughput, 1 connection vs Elasticsearch | 3x higher queries per second, 3x lower query times | ParadeDB's own benchmark — vendor numbers |
| Throughput, 40 connections vs Elasticsearch | 5x higher throughput, 5x lower latency | ParadeDB's own benchmark — vendor numbers |
| ClickBench with pg_analytics parquet tables | 94x faster than regular Postgres, 8x faster than Elasticsearch, "almost ties ClickHouse" | ParadeDB's own benchmark — vendor numbers |
What Is pg_analytics, the Second Extension?
The ParadeDB analytics extension, pg_analytics, turns any Postgres instance into an analytical database using Apache Arrow and DataFusion, without moving your data anywhere. That is nearly verbatim from ParadeDB's own description: pg_analytics "uses Apache Arrow and DataFusion to transform any Postgres instance into a state-of-the-art analytical (i.e. OLAP) database." The beginner experience is three lines: CREATE EXTENSION pg_analytics, then CREATE TABLE movies (name text, rating int) USING parquet, then ordinary queries like SELECT AVG(rating) FROM movies — and the verbatim promise is that "parquet tables accept any Postgres query." In other words, you get columnar analytical storage addressable with the SQL you already know. It is a separate extension in its own repository (paradedb/pg_analytics), and there is a Helm chart repository (paradedb/charts) for the Kubernetes lane. One scope line, said once: pg_search is the search extension and pg_analytics is the analytics extension — they are siblings, not substitutes, and most beginners should get comfortable with pg_search first before opening the analytics lane.
When Should You Keep Elasticsearch Anyway?
You keep Elasticsearch anyway when your search workload outgrows what one Postgres instance can comfortably serve — and knowing that boundary is the honest half of this beginner guide. The ParadeDB Elasticsearch alternative framing is honest for single-instance workloads, and this section draws its boundary. ParadeDB's own framing is "the best of both worlds, giving Postgres users the performance of a dedicated search and analytical database with zero new infrastructure or data movement into another system," and for single-instance apps, internal tools, and products whose search corpus lives in Postgres already, that trade is usually correct. But a dedicated search cluster still wins when you need multi-datacenter replication of the search tier, dozens of specialized analyzers across many languages, or query volumes that would starve your transactional Postgres of resources — the same one-index-per-table and single-instance realities that make ParadeDB simple also bound it. This is also where the lane lines matter: Meilisearch, the standalone search server we cover in our Meilisearch beginner guide, replaces your search tier entirely — it is a separate engine you deploy and query — while ParadeDB keeps search inside the Postgres you already run. The ParadeDB vs Elasticsearch decision comes down to one question: does your data already live in Postgres, and does your team already operate Postgres? If yes to both, ParadeDB's zero-new-infrastructure trade is usually correct. Use ParadeDB when your data already lives in Postgres and your team already operates Postgres; reach for a dedicated search cluster when search itself becomes the scaling problem.
What Are the 7 Mistakes Beginners Make with ParadeDB?
Beginners make the same seven mistakes with ParadeDB, and each one has a one-line fix. The table below is the original citable asset for this guide — built from the verified facts above, not from folklore.
| Mistake | What happens | The fix |
|---|---|---|
| Writing USING bm25 from old blog posts | Works today via the alias, but you are learning a legacy name | Use USING paradedb on 0.25 and later |
| Copying WITH (key_field=...) into a 0.26 index | A deprecation WARNING on every index build | Omit key_field on 0.26 — it is a no-op |
| Forgetting pgvector before CREATE EXTENSION | Enable fails on 0.25+ (hard dependency) | Use CREATE EXTENSION pg_search CASCADE |
| Confusing @@@ with Postgres @@ | Queries hit the wrong engine or fail | @@@ = ParadeDB's ParadeQL on the index; @@ = native tsvector |
| Building two pg_search indexes on one table | "ERROR: a relation may only have one ParadeDB index" | Plan all indexed columns in a single CREATE INDEX |
| Treating vendor benchmarks as independent | You size hardware on marketing numbers | Label the 2.5x/3x/5x figures as ParadeDB's own; trust the independent dev.to numbers for version-to-version changes |
| Skipping the stemmer for your language | Stemming tuned to English silently hurts other languages | Match pdb.simple('stemmer=...') to your corpus language before indexing at scale |
Sources
- ParadeDB install docs, "Get started locally with our Curlbash installer, Docker, or Postgres.app" — paradedb.com/docs/start/install (page updated September 30, 2026)
- ParadeDB upgrading docs, "The latest version of pg_search is 0.26.4" — paradedb.com/docs (updated October 29, 2026); release date October 29, 2026 per newreleases.io
- ParadeDB README — prebuilt binaries for Postgres 14–17 on Debian 12, Ubuntu 22.04/24.04, RHEL 8/9; "supports all versions supported by the PostgreSQL Global Development Group, which includes PostgreSQL 13+" — github.com/paradedb/paradedb
- Docker Hub — tags paradedb/paradedb:0.26.4-pg17 and :0.26.4-pg16, updated days before November 2, 2026 — hub.docker.com
- ParadeDB hybrid-search blog — current index syntax with USING paradedb and pdb.simple('stemmer=english') tokenizers — paradedb.com/blog/hybrid-search-in-postgresql-the-missing-manual (October 22, 2025)
- blog.rasc.ch — "ParadeDB 0.25 renamed the access method from bm25 to paradedb; USING bm25 remains a backwards-compatible alias"; Docker image "includes everything you need to get started" (2026)
- dev.to/alexgeorgiev17 — pg_search 0.26.0 benchmark: ten-term OR ~129ms to ~29ms, single-term regression ~1.4ms to ~8ms, "key_field is deprecated as of 0.26.0 and is a no-op", "ERROR: a relation may only have one ParadeDB index", "If your search boxes mostly take one or two words, this upgrade is a wash at best for you" (October 8, 2026)
- ParadeDB introducing-search blog — "We chose the @@@ operator to signify the beginning of a query to the ParadeDB index in homage to the @@ operator used by Postgres' native full text search" — paradedb.com/blog/introducing-search (November 15, 2023)
- star-history.com/blog/paradedb — "ParadeDB is an Elasticsearch alternative built on Postgres. Our two extensions, pg_search and pg_analytics, are drop-in solutions for full-text search and analytics inside Postgres"; "We're open source and built in 100% Rust"; vendor benchmark figures (April 18, 2024)
- ParadeDB personalized-search blog — retrieve-and-rerank in SQL: BM25 top-100 then cosine rerank, 0.5 * normalized_bm25 + 0.5 * personal_score; "Move the compute to the data, not the data to the compute" — paradedb.com/blog/personalized-search-in-postgresql (January 21, 2026)
- blog.brightcoding.dev — ParadeDB explainer: AGPL-3.0 license with enterprise add-on pack; 7,000+ stars (September 2025)
- linuxlinks — ParadeDB license listing: GNU Affero General Public License v3.0
- enterprisedna directory — 8,883 stars snapshot (June 1, 2026)
- drupal.org/project/search_api_paradedb — Search API ParadeDB module: CREATE EXTENSION pg_search CASCADE; BEIR TREC-COVID benchmark reference
Frequently Asked Questions
Is ParadeDB a replacement for Elasticsearch?
It is a replacement for Elasticsearch workloads that live inside a single Postgres instance — internal search, product search over your own tables, analytics on data you already hold. ParadeDB's own benchmarks claim 2.5x faster indexing and 3–5x the query throughput of Elasticsearch on the same corpus, but those are vendor numbers; the independent October 2026 benchmark covers version-to-version changes, not Elasticsearch head-to-heads. Keep Elasticsearch when search itself becomes the scaling problem: multi-datacenter search tiers, extreme query volumes, or analyzer needs beyond what ParadeDB ships.
Does ParadeDB work on regular Postgres, or do I need the Docker image?
Regular Postgres works. The official README publishes prebuilt binaries for Postgres 14 through 17 on Debian 12, Ubuntu 22.04 and 24.04, and RHEL 8 and 9, and states support for every version the PostgreSQL Global Development Group supports — PostgreSQL 13 and up. The Docker image is simply the fastest way to start, not a requirement.
What is the difference between @@@ and @@ in Postgres?
@@ is Postgres's native full-text search operator against tsvector columns. @@@ is ParadeDB's ParadeQL operator against a pg_search BM25 index — chosen, in ParadeDB's own words, "in homage to" the native operator. They are different engines behind similar punctuation: @@@ queries accept Tantivy-style syntax like field:term^2 boosting and fuzzy clauses, and ranking comes from paradedb.score(key).
Why does my index build print a key_field deprecation warning?
Because key_field became a deprecated no-op in pg_search 0.26.0. The warning — "WARNING: key_field is deprecated as of 0.26.0 and is a no-op" — is harmless, but you should stop copying WITH (key_field=...) from older examples and omit it from new index definitions. While you are updating old examples, switch USING bm25 to USING paradedb as well.
Can I put two pg_search indexes on the same table?
No. A table is capped at one pg_search index; attempting a second raises "ERROR: a relation may only have one ParadeDB index." Plan all the columns you want searchable into a single CREATE INDEX statement, using column casts like pdb.simple('stemmer=english') to configure each column's tokenizer.