Skip to content
Go back

Qdrant vs Weaviate vs Chroma: Vector DB Showdown

By SumGuy 7 min read
Qdrant vs Weaviate vs Chroma: Vector DB Showdown

The Vector DB Trap

You’ve built a RAG chatbot. You threw embeddings into ChromaDB, it works great on your laptop, and you ship it. Then production hits. You need filtering by date range, backups that don’t require copying Python dictionaries, or query performance that doesn’t crater when you scale past 10,000 documents.

That’s when you realize: there’s more than one way to store vectors. A lot more.

ChromaDB is fine for prototypes. But Qdrant brings Rust-fast search with serious filtering. Weaviate adds schema validation and hybrid search. The three aren’t interchangeable — they’re answering different questions about what a vector database needs to be.

Let’s build something with each one, see where they shine, and figure out which one you actually need.

What Vector Databases Actually Do (The Brief Version)

A vector database stores embeddings (dense numerical vectors from an LLM) and answers one question fast: “Give me the N documents most similar to this query vector.”

That’s the core job. Everything else — filtering, persistence, replication, schema enforcement — is about how well they do it in production.

ChromaDB: The Dead Simple Choice

The pitch: Zero setup, lives in your Python process, perfect for notebooks and prototypes.

Reality: It works, until it doesn’t. No persistence out of the box (it writes to disk but forgets everything on restart if you’re not careful). Filtering is basic. Performance drops fast when you hit scale. But for a proof-of-concept? Chef’s kiss.

ChromaDB Docker Setup

docker-compose.yml
version: "3.8"
services:
chroma:
image: ghcr.io/chroma-core/chroma:latest
ports:
- "8000:8000"
volumes:
- chroma_data:/chroma/data
environment:
IS_PERSISTENT: "TRUE"
volumes:
chroma_data:

Spin it up:

Terminal window
docker compose up -d

ChromaDB Python Example

import chromadb
# Connect to server
client = chromadb.HttpClient(host="localhost", port=8000)
# Create collection
collection = client.get_or_create_collection(name="articles")
# Add documents with embeddings
collection.add(
ids=["doc1", "doc2"],
documents=["Docker networking is complex", "Kubernetes is overkill for home labs"],
metadatas=[{"topic": "docker"}, {"topic": "k8s"}],
)
# Query (Chroma generates embeddings automatically with default model)
results = collection.query(
query_texts=["Tell me about container networking"],
n_results=1,
)
print(results["documents"])

Simple. Boring. Works.

Qdrant: The Production Speed Demon

The pitch: Written in Rust, built for speed. Filtering works. Snapshots for backup. REST and gRPC APIs.

Reality: It’s a real database. More to learn, but you’re not wondering if it’ll survive a restart or scale beyond 100K vectors.

Qdrant is what you graduate to when ChromaDB starts feeling like a toy. It’s fast because it’s compiled, well-designed, and doesn’t compromise on filtering.

Qdrant Docker Setup

docker-compose.yml
version: "3.8"
services:
qdrant:
image: qdrant/qdrant:latest
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant_storage:/qdrant/storage
environment:
QDRANT_API_KEY: "your-secret-key"
volumes:
qdrant_storage:

Qdrant Python Example

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
import numpy as np
client = QdrantClient(url="http://localhost:6333", api_key="your-secret-key")
# Create collection with vector dimension (768 for common embeddings)
client.create_collection(
collection_name="articles",
vectors_config=VectorParams(size=768, distance=Distance.COSINE),
)
# Add points with filtering metadata
client.upsert(
collection_name="articles",
points=[
PointStruct(
id=1,
vector=np.random.rand(768).tolist(),
payload={"title": "Docker Compose", "topic": "docker", "year": 2026},
),
PointStruct(
id=2,
vector=np.random.rand(768).tolist(),
payload={"title": "Kubernetes Operators", "topic": "k8s", "year": 2025},
),
],
)
# Search with filtering (year >= 2025)
results = client.search(
collection_name="articles",
query_vector=np.random.rand(768).tolist(),
query_filter={
"must": [
{
"key": "year",
"range": {"gte": 2025},
}
]
},
limit=5,
)
for hit in results:
print(f"{hit.payload['title']} (score: {hit.score})")

See that query_filter? That’s why people choose Qdrant. Filtering isn’t a hack — it’s baked in.

Why Qdrant Wins on Performance

Trade-off: You need to manage another service. But if you’re self-hosting anyway, that’s just one more container.

Weaviate: The ORM for Vectors

The pitch: Schema-first, GraphQL queries, built-in vectorizers (no separate embedding step), can do hybrid search (keyword + semantic together).

Reality: Heavier on resources, more opinionated about structure. Powerful if you need it, overkill if you don’t.

Weaviate lets you define your data structure upfront. It can generate embeddings for you on ingest. It can search using both traditional keywords and vectors in one query. It’s almost like an ORM met a vector database and had a very overqualified baby.

Weaviate Docker Setup

docker-compose.yml
version: "3.8"
services:
weaviate:
image: semitechnologies/weaviate:latest
ports:
- "8080:8080"
- "50051:50051"
volumes:
- weaviate_data:/var/lib/weaviate
environment:
QUERY_DEFAULTS_LIMIT: 100
AUTHENTICATION_APIKEY_ENABLED: "true"
AUTHENTICATION_APIKEY_ALLOWED_KEYS: "weaviate-key"
DEFAULT_VECTORIZER_MODULE: "text2vec-openai"
OPENAI_APIKEY: ${OPENAI_API_KEY}
volumes:
weaviate_data:

You’ll need an OpenAI key, but Weaviate also supports Hugging Face, Cohere, and local vectorizers.

Weaviate Python Example

import weaviate
from weaviate.classes.config import Configure
client = weaviate.connect_to_local(
host="localhost",
port=8080,
grpc_port=50051,
)
# Define schema (like creating a table)
client.collections.create(
name="Article",
description="Blog articles with metadata",
vectorizer_config=Configure.Vectorizer.text2vec_openai(),
properties=[
Configure.Property.text(name="title", description="Article title"),
Configure.Property.text(name="body", description="Article content"),
Configure.Property.text(name="topic", description="Category"),
Configure.Property.int(name="year", description="Publication year"),
],
)
# Add objects (schema enforced)
articles = client.collections.get("Article")
articles.data.insert_multiple(
[
{
"title": "Docker Compose Secrets",
"body": "How to handle sensitive data in Compose files...",
"topic": "docker",
"year": 2026,
},
{
"title": "Kubernetes Node Affinity",
"body": "Controlling pod placement with node selectors...",
"topic": "k8s",
"year": 2025,
},
]
)
# GraphQL query (hybrid search: keyword + semantic)
response = articles.query.hybrid(
query="Docker networking and secrets",
where={
"path": ["year"],
"operator": "GreaterThanEqual",
"valueInt": 2025,
},
limit=5,
)
for obj in response.objects:
print(f"{obj.properties['title']} (score: {obj.metadata.score})")
client.close()

Weaviate’s GraphQL layer is both its strength and its barrier. Powerful, but you need to learn GraphQL syntax if you haven’t already.

The Filtering Difference Matters

ChromaDB: Supports where-clauses, but they’re basic. Good enough for “is this tag present” or “is this number in range” — but performance degrades with complex filters on large datasets.

Qdrant: Filtering is native. Complex nested filters, range queries, all work efficiently because the index is aware of metadata from the start.

Weaviate: Filtering works through GraphQL, which is clean once you learn it. Performance is solid because Weaviate is designed for it.

Persistence and Backup

FeatureChromaDBQdrantWeaviate
PersistenceDisk (HTTP mode)Snapshots built-inPersistent volumes
Backup storyCopy the directorysnapshot_create APIDocker volume snapshots
Point-in-time recoveryNoVia snapshotsNo
ReplicationNoCluster mode (paid)No

Decision Time

Use ChromaDB if:

Use Qdrant if:

Use Weaviate if:

The Real Talk

Most RAG projects that fail do so because they outgrew ChromaDB around month three and didn’t have a plan to migrate. Qdrant doesn’t have that problem. You can start with Chroma, port to Qdrant in a weekend, and sleep better knowing snapshots exist.

Weaviate is the choice when you’re building something that’s half-vector, half-traditional search. If you’re 90% vectors and 10% keyword search, Qdrant + Elasticsearch is cleaner than fighting Weaviate’s schema opinions.

Pick the simplest tool that doesn’t make you want to scream in production. That’s usually Qdrant.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
n8n vs Node-RED: Automate Everything Without Learning to Code (Much)
Next Post
CUDA vs ROCm vs CPU: Running AI on Whatever GPU You've Got

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts