pgvector Explained: Vector Search Inside PostgreSQL

For most AI apps, the vector database you need is the Postgres you already have. What pgvector is, how HNSW and IVFFlat indexes trade off, why filtered search is the quiet superpower, and the honest ceiling where dedicated vector stores win.

TL;DR
pgvector adds a vector column type plus HNSW and IVFFlat indexes to Postgres, handling similarity search for millions of embeddings alongside your relational data - with the joins, filters and transactions dedicated vector stores make you reimplement. Purpose-built vector databases earn their place past the tens-of-millions scale or for specialized recall and latency demands.
What you’ll learn
  • What embeddings and similarity search actually are, in one section
  • The vector type, distance operators, and how to pick between them
  • HNSW vs IVFFlat: what each index spends and buys
  • Filtered vector search - the thing Postgres does better than dedicated stores
  • The honest ceiling where a dedicated vector database wins

pgvector is a PostgreSQL extension that adds vector similarity search to the database you already run - a

sql
vector
column type, distance operators, and two index families that make nearest-neighbour queries fast at scale. It exists because the AI wave turned 'find the most similar items' from a niche requirement into the core query of every RAG pipeline, semantic search box and recommendation feature - and because standing up a separate vector database for that query is usually architecture theatre.

Embeddings, in one section

An embedding model turns content - text, images, code - into a list of numbers (a vector, typically 384 to 3,072 dimensions) where semantic similarity becomes geometric closeness. 'Reset my password' and 'I can't log in' land near each other in that space despite sharing no words. Similarity search is then just: given a query vector, find the stored vectors closest to it. That is the entire trick behind RAG, semantic search and 'related items'.

How pgvector turns Postgres into a vector databaseText becomes embeddings via a model, embeddings are stored in a vector column, and similarity queries return nearest neighbours alongside relational dataVector search inside the database you already runYour contentdocs, products, ticketsEmbedding modeltext to vectorsPostgres + pgvectorvector column + indexORDER BY embedding <=> query LIMIT 10 - with joins, filters andtransactions the dedicated vector stores make you reimplement.
Content becomes vectors via an embedding model; pgvector stores them in a column and answers nearest-neighbour queries with an ORDER BY.

The type and the operators

Enable the extension, add a column, insert vectors from your embedding model:

sql
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE docs (
  id        bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  content   text NOT NULL,
  tenant_id bigint NOT NULL,
  embedding vector(1536)  -- match your model's dimensions
);

-- nearest neighbours by cosine distance
SELECT id, content
FROM docs
ORDER BY embedding <=> $1
LIMIT 10;

Three distance operators cover the standard metrics:

sql
<->
(Euclidean),
sql
<=>
(cosine distance) and
sql
<#>
(negative inner product). For normalized embeddings - which is what most current models produce - cosine and inner product rank identically; pick one and use the same metric in the index and the query, or the index will not be used.

Indexes: HNSW vs IVFFlat

Without an index, every similarity query scans every row - exact, but linear. Both pgvector index families make search approximate and fast; they spend resources differently.

HNSW versus IVFFlat index trade-offs in pgvectorHNSW gives better recall and query speed at higher build cost and memory; IVFFlat builds fast and suits static dataThe two index familiesBoth make similarity search approximate and fast. They spend differently.HNSWQuery speed / recallexcellentBuild time + memoryexpensiveIVFFlatQuery speed / recallgood, tunableBuild time + memorycheap
HNSW buys better recall and latency with expensive builds and more memory; IVFFlat builds cheaply and suits static datasets.

HNSW

IVFFlat

Query speed / recall

Excellent

Good, tunable via probes

Index build

Slow, memory-hungry

Fast

Data that changes often

Handles it well

Recall degrades; rebuild periodically

Empty-table start

Works (build as you insert)

Needs data first (trains on a sample)

Default choice?

Yes, for most workloads

Large, mostly-static datasets

sql
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops);

Filtered search: the quiet superpower

Real applications never search all vectors - they search this tenant's documents, published articles, products in stock. In a dedicated vector store, combining filters with similarity is a feature you configure and often fight. In Postgres it is a WHERE clause:

sql
SELECT id, content
FROM docs
WHERE tenant_id = $2          -- just SQL
ORDER BY embedding <=> $1
LIMIT 10;

And because the vectors live in the same transactional database as the rows they describe, there is no sync pipeline to build and no window where a deleted document still surfaces in search results. Delete the row, the embedding is gone - atomically. That single property eliminates a whole class of RAG bugs.

A working RAG schema

Retrieval-augmented generation needs exactly three things from the database: store chunks with their embeddings, retrieve the nearest chunks for a question, and keep them scoped to the right user or tenant. In Postgres that is one table and one query:

sql
CREATE TABLE chunks (
  id         bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  source_id  bigint NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
  tenant_id  bigint NOT NULL,
  body       text NOT NULL,
  embedding  vector(1536) NOT NULL
);
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON chunks (tenant_id);

-- retrieval for one tenant's question
SELECT body FROM chunks
WHERE tenant_id = $1
ORDER BY embedding <=> $2
LIMIT 8;

The ON DELETE CASCADE line is doing more work than it looks like: when a source document is removed, its chunks and embeddings vanish in the same transaction. In a two-database architecture that guarantee is a sync pipeline you have to build, monitor and apologise for.

Tuning that actually matters

  • Index build memory. Set maintenance_work_mem generously before CREATE INDEX on a large table - HNSW builds are dramatically faster when they fit in memory.

  • Recall vs speed at query time. hnsw.ef_search (default 40) is the dial: raise it for better recall, lower it for latency. Test against a labelled sample rather than guessing.

  • Dimensions cost real money. 1,536 dimensions is ~6 KB per row; models with 3,072 double that. If your model supports shortened embeddings, smaller often loses little recall and halves storage and index size.

  • Vacuum still applies. Embeddings update when content changes; dead tuples bloat the index like any other. Ordinary Postgres hygiene carries over unchanged.

Where the honest ceiling is

pgvector comfortably serves millions of vectors on ordinary hardware - HNSW queries in single-digit milliseconds, with memory (the index wants to live in RAM) as the main sizing constraint. The dedicated stores - Pinecone, Qdrant, Weaviate, Milvus - earn their place past tens of millions of vectors, at very high query throughput, or for specialized capabilities like tuned hybrid reranking at scale. If you are not sure whether you are past the ceiling, you are not past it.

The mistakes everyone makes once

  • Mismatched metrics. Building the index with vector_l2_ops and querying with <=> means the index is silently ignored and every query scans the table. Same metric in both places, always.

  • Forgetting the LIMIT. An approximate index answers 'top K nearest'; a similarity query without LIMIT degenerates into sorting the whole table.

  • Embedding at query time, twice. Cache the query embedding if the same question feeds retrieval and reranking - embedding API calls are the slow, billable part of the pipeline.

  • Testing recall on toy data. Ten sample rows make every index look perfect. Evaluate against a realistic corpus before promising quality.

Running it in production

pgvector is among the most widely supported extensions on managed Postgres platforms - though each provider allows a vetted list, so verify before committing (our PostgreSQL hosting comparison covers how to evaluate providers generally). Size RAM for the index, monitor recall if your data churns, and remember the embedding column dominates storage: 1,536 floats is ~6 KB per row before the text itself. A million embedded documents is a perfectly ordinary Postgres database - which is precisely the point.

Prices quoted here were checked in July 2026. Vendors change them; treat the structure as the durable part and re-check the current numbers before you budget.

Summary
The default answer to 'which vector database should I use' is the database you already operate. pgvector makes similarity search a column type and an index rather than a second system with its own consistency model, billing and failure modes. Reach for a dedicated store when scale or latency genuinely demands it - not because the architecture diagram looked more impressive with another box.

Frequently asked questions

Is pgvector good enough for production RAG?

For most applications, yes - millions of embeddings with HNSW indexing and metadata filtering in plain SQL covers the typical RAG workload comfortably, and keeps the vectors transactionally consistent with the data they describe.

Should I use HNSW or IVFFlat?

HNSW for most cases: better recall and query latency at the cost of slower index builds and more memory. IVFFlat builds much faster and suits datasets that are large but mostly static.

When do I need a dedicated vector database?

Past tens of millions of vectors, at very high query throughput, or when you need specialized features like built-in hybrid reranking at scale - then Pinecone, Qdrant or Weaviate earn their operational overhead.

Does pgvector work on managed Postgres hosting?

On most providers, yes - it is one of the most widely supported extensions. Check the provider's extension list before committing, since managed platforms each allow a vetted subset.

Share this articleXLinkedInHacker News

Related articles

MongoDB vs PostgreSQL: How to Actually Choose in 2026

Postgres learned documents (JSONB) and Mongo learned transactions - the old dividing lines moved. A comparison built on what still differs: defaults,…

Read more
PostgreSQL Connection Strings Explained (DATABASE_URL)

A PostgreSQL connection string encodes who you are, where the database lives, and how to reach it safely - all in one line. The full format, the…

Read more
Best Database for AI-Generated Apps: Lovable, Bolt, v0, Cursor

AI builders create apps that outlive their preview sandboxes - then the database question arrives. Supabase, Neon, Firebase, Swyftstack and local…

Read more

All posts