Recommended Open source
Use pgvector in PostgreSQL before a separate vector database
Store embeddings in a pgvector column next to the rows they describe. Add a separate vector database only after a load test shows a limit PostgreSQL cannot meet.
Cost
Open source. PostgreSQL licence, free to run on your own servers. Managed PostgreSQL hosting is paid.
How to do it
pgvector is a PostgreSQL extension under the PostgreSQL licence. Its first release, 0.1.0, came out on 20 April 2021, and 0.8.7 came out on 1 October 2026. It has no 1.0 yet, but it has five years of production use. Its Python package had about 9 million weekly PyPI downloads in October 2026. One database means one backup, one set of permissions and plain joins between vectors and your data. It has HNSW and IVFFlat indexes with cosine, L2 and inner product distance. Keyword search lives in the same database, so Search by meaning and by words stays one query. Choose something else when a load test shows vector queries slowing your main database, or you must spread vectors over several machines. Then pick a vector database with an OSI licence and a tested export.
Examples
Using these examples: they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.
SQL example
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE articles ADD COLUMN embedding vector(1536);
CREATE INDEX ON articles USING hnsw (embedding vector_cosine_ops);
TypeScript example
import pg from 'pg';
import pgvector from 'pgvector/pg';
const RESULT_COUNT = 20;
const client = new pg.Client();
await client.connect();
await pgvector.registerTypes(client);
export async function nearestArticles(embedding: number[]) {
const { rows } = await client.query(
'SELECT id, title FROM articles ORDER BY embedding <=> $1 LIMIT $2',
[pgvector.toSql(embedding), RESULT_COUNT],
);
return rows;
}
Python example
import numpy as np
import psycopg
from pgvector.psycopg import register_vector
RESULT_COUNT = 20
connection = psycopg.connect()
register_vector(connection)
def nearest_articles(embedding: list[float]) -> list[tuple]:
return connection.execute(
"SELECT id, title FROM articles ORDER BY embedding <=> %s LIMIT %s",
(np.array(embedding), RESULT_COUNT),
).fetchall()
How to check
A load test with your expected vector count and query rate meets your latency target on PostgreSQL with an HNSW index. A decision record holds those numbers before any separate vector database is added.
Related rules
- Search by meaning and by words in AI features
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. Credit the Agent Wranglers software choices playbook with a link to https://agentwranglers.se/playbook/stack/ai-software/pgvector-first. Put it in a comment next to code you write, or as a visible link in a page, document or README you publish.