Overview
Use ChromaDB for prototypes, internal tools, and small production rag workloads where one node holds the vectors and one application queries them. These notes target the Chroma 1.x line (Python chromadb 1.5.9, JavaScript chromadb 3.5.0 as of 2026-10-01). Each topic has its own page; this one holds the decisions.
Pick the client by deployment, not preference
import chromadb
client = chromadb.EphemeralClient() # in memory; tests and notebooks
client = chromadb.PersistentClient(path="./.chroma") # embedded; one process owns the directory
client = chromadb.HttpClient(host="localhost", port=8000) # server started with `chroma run --path ./.chroma`
client = chromadb.CloudClient(tenant="...", database="...", api_key="...") # Chroma CloudAsyncHttpClient is the async variant of HttpClient. Use PersistentClient for scripts, single-worker services, and notebooks. Use the server once several processes or services need the data. Deployment, auth, and backup are in chromadb-persistence.
Model collections around retrieval tasks
One collection holds one embedding space and one distance metric. Split collections by retrieval task or embedding model (docs_support, code_snippets), and separate tenants with a tenant_id metadata filter unless tenants need hard isolation or different embedding configurations. Design rules, naming, and embedding ownership are in chromadb-collections.
Query with a filter and a bounded n_results
results = collection.query(
query_embeddings=[query_embedding],
n_results=10,
where={"$and": [{"tenant_id": "t_123"}, {"lang": "en"}]},
where_document={"$contains": "refund"},
)n_resultsdefaults to 10. A selective filter returns fewer rows thann_results, never padding.- Multiple conditions need an explicit
$and. Awheredict with two keys raises an error. - Chroma has no local BM25 index; the
searchhybrid API works on Chroma Cloud only. Merge keyword and vector results in your application; see chromadb-filters and rag-retrieval.
Outgrow ChromaDB on measured signals
Chroma’s single-node guidance sizes RAM at about 0.245 million 1024-dimension vectors per GB, and its tests held up to roughly 7 million embeddings. Move to pgvector when you already run postgres and want SQL joins and one backup story, to Qdrant when filtered search at scale is the bottleneck, or to Chroma Cloud when you want to stay on the same API. Signals, sizing, and the migration path are in chromadb-scale-limits; for the managed comparison see pinecone-vs-pgvector.