How to Use Qdrant: The Beginner's Guide (First Collection, First Vectors, Payload Filters, and the Dimension Decision)

CodexFuse Team · 2026-10-31T09:12:30.004Z · 15 min read

Learn how to use Qdrant: start the vector database with Docker, create your first collection, upsert vectors with payloads, and run filtered similarity search.

If you are building semantic search or a RAG app, the piece you are missing is a database that thinks in meaning instead of keywords — and learning how to use Qdrant is the fastest way to get one. Qdrant is an open-source vector database that stores embedding vectors alongside plain JSON metadata, then finds the stored vectors closest to any query vector in milliseconds. This beginner's guide runs the full first-use arc in one sitting: picking an embedding model and its dimension, starting Qdrant with Docker, creating your first collection, upserting your first vectors, running your first search, filtering by payload, and wiring it into a real RAG app. If you plan to generate your embeddings locally instead of calling a paid API, skim the Ollama beginner guide first — Ollama can run the embedding models this guide plugs into Qdrant.

What Is Qdrant, in One Paragraph?

Qdrant (pronounced "quadrant") is an open-source vector database and similarity-search engine written in Rust and released under the Apache 2.0 license. You run it as a server — usually with Docker — and talk to it over a REST API on port 6333 or gRPC on port 6334, with official client libraries for Python, JavaScript/TypeScript, Rust, Go, Java, and C#. A collection in Qdrant works like a table in a database; every row (called a point) carries an ID, one vector, and a payload of JSON metadata you can filter on. Its headline uses are semantic search, retrieval-augmented generation (RAG), recommendations, and image similarity search. The whole first-use arc is six steps:

  1. Start Qdrant with one Docker command on ports 6333/6334.
  2. Decide your embedding model and its vector dimension (384 for all-MiniLM-L6-v2, 1536 for text-embedding-ada-002).
  3. Create your first collection with a PUT request, locking in that dimension and a distance metric.
  4. Upsert your first points — vector plus payload — into the collection.
  5. Run your first search with the new query_points API.
  6. Add a payload index on any field you filter on, then search with filters.

Which Embedding Model Should I Pick First?

Pick the embedding model before you create the collection, because the vector dimension must match the model exactly — create a 384-dimension collection and you cannot later store 1536-dimension vectors in it; you would have to recreate the collection from scratch. Vectors from different models live in different mathematical spaces, so never mix models inside one collection.

The two safe starter choices are the ones the ecosystem documents most heavily. Qdrant's own official tutorials use sentence-transformers/all-MiniLM-L6-v2 (384 dimensions) for local embeddings, and the quantization docs benchmark OpenAI's text-embedding-ada-002 (1536 dimensions) and the text-embedding-3 family (512–3072 dimensions). This table makes the decision concrete:

ModelDimensionsWhere it runsBest for
all-MiniLM-L6-v2384Locally (free, no API key)First experiments, self-hosted RAG, small corpora
text-embedding-ada-0021536OpenAI API (paid)Production quality without running a model yourself
text-embedding-3-small512–1536OpenAI API (paid)Cheaper than ada-002, strong benchmark numbers
text-embedding-3-large1024–3072OpenAI API (paid)Maximum recall on large corpora

This guide uses 384 dimensions (all-MiniLM-L6-v2) because it keeps every example runnable on your own machine with zero API keys. If you want a local model server rather than the sentence-transformers Python package, the Ollama beginner guide shows how to run embedding models locally too.

How Do I Install Qdrant with Docker?

The official local quickstart installs Qdrant with two Docker commands: pull the image, then run it with the REST port (6333), the gRPC port (6334), and a volume so your data survives container restarts.

docker pull qdrant/qdrant

docker run -p 6333:6333 -p 6334:6334 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
    qdrant/qdrant

The volume mount is the one step beginners skip and later regret: without it, every collection lives inside the container's filesystem and vanishes the moment the container is removed. Under the default configuration all data goes into /qdrant/storage, so mounting $(pwd)/qdrant_storage there is the persistence rule. Qdrant is now reachable at http://localhost:6333 for REST, http://localhost:6333/dashboard for the built-in web UI, and localhost:6334 for gRPC.

For anything beyond a weekend experiment, pin a version tag instead of the rolling image — v1.20.0 was the current release at the time of writing (released October 22, 2026, per the project's GitHub releases) — and put the ports behind authentication or a reverse proxy before exposing them. If Docker is new to you, the Docker beginner guide covers volumes and port mapping in detail.

How Do I Create My First Collection?

Creating a collection is always a PUT request — one of Qdrant's verbs you must get right, because search is a POST and the two are not interchangeable. The call names the collection, sets the vector dimension, and picks the distance metric that scores similarity. Open the dashboard at http://localhost:6333/dashboard or use the REST API directly:

curl -X PUT http://localhost:6333/collections/my_docs \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "vectors": {
      "size": 384,
      "distance": "Cosine"
    }
  }'

The Python client does the same thing with friendlier names:

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams

client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    collection_name="my_docs",
    vectors_config=VectorParams(size=384, distance=Distance.COSINE),
)

The distance metric decides what "similar" means. Cosine compares the angle between vectors (the standard pick for text embeddings and the choice in this guide); Dot compares raw dot product (what the official quickstart uses); Euclid compares straight-line distance; Manhattan compares coordinate-by-coordinate distance (and is the slowest option for quantization). For text embeddings, Cosine is the safe default. The size, 384, matches all-MiniLM-L6-v2 — the decision you made in the previous section.

How Do I Add My First Vectors?

You add data with an upsert — "upsert" means insert-or-update, so re-uploading a point with an existing ID replaces it, which makes re-indexing a changed document trivial. Each point is a JSON object with an id, a vector, and a payload of associated metadata you will later filter on:

curl -X PUT http://localhost:6333/collections/my_docs/points \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "points": [
      { "id": 1, "vector": [0.05, 0.61, 0.76, 0.74], "payload": { "city": "Berlin", "category": "travel" } },
      { "id": 2, "vector": [0.19, 0.81, 0.75, 0.11], "payload": { "city": "London", "category": "travel" } },
      { "id": 3, "vector": [0.36, 0.55, 0.47, 0.94], "payload": { "city": "Paris", "category": "food" } }
    ]
  }'

The Python client equivalent, straight from the official quickstart:

from qdrant_client.models import PointStruct

operation_info = client.upsert(
    collection_name="my_docs",
    wait=True,
    points=[
        PointStruct(id=1, vector=[0.05, 0.61, 0.76, 0.74], payload={"city": "Berlin", "category": "travel"}),
        PointStruct(id=2, vector=[0.19, 0.81, 0.75, 0.11], payload={"city": "London", "category": "travel"}),
        PointStruct(id=3, vector=[0.36, 0.55, 0.47, 0.94], payload={"city": "Paris", "category": "food"}),
    ],
)

print(operation_info)

Two details matter here. First, wait=True tells the client to wait for the write to complete before continuing — without it, an immediate search can miss points that have not finished indexing yet. Second, store the original text in the payload alongside every chunk: Qdrant returns the payload with each search hit, so you do not need a second database lookup to show the passage to the user.

How Do I Run My First Search?

Search is a POST, and the current client API is query_points — the older method name search was renamed in newer clients, so use query_points in Python and query in the JavaScript client. The REST endpoint is POST /collections/{collection_name}/points/query. Here is the REST version of asking "which stored vectors are most similar to this query vector":

curl -X POST http://localhost:6333/collections/my_docs/points/query \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "query": [0.2, 0.1, 0.9, 0.7],
    "limit": 3
  }'

And the Python version, matching the current official quickstart:

search_result = client.query_points(
    collection_name="my_docs",
    query=[0.2, 0.1, 0.9, 0.7],
    limit=3,
).points

print(search_result)

Results come back in decreasing similarity order, each with an id, a score, and — unless you asked for them — no payload or vector data, to keep responses small. In real code the query vector is not typed by hand: you embed the user's question with the same model you used for the documents (here, all-MiniLM-L6-v2), which is exactly what the RAG section below does end to end.

What Is the Payload-Filter Rule?

The payload-filter rule is the single habit that separates fast Qdrant deployments from slow ones: create a payload index on every field you filter on, before you ingest data. Qdrant's own filtering guide states it plainly — the vector index accelerates vector search, the payload index accelerates filtering, and without an index Qdrant has to read payloads to evaluate the filter while the query planner cannot even estimate how many points will match. Here is a search filtered to one category, exactly as the official docs show it:

curl -X POST http://localhost:6333/collections/my_docs/points/query \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "query": [0.2, 0.1, 0.9, 0.7],
    "filter": {
      "must": [
        { "key": "category", "match": { "value": "travel" } }
      ]
    },
    "limit": 3
  }'

And the index that makes that filter fast, created once per field:

curl -X PUT http://localhost:6333/collections/my_docs/index \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "field_name": "category",
    "field_schema": "keyword"
  }'

Match the index type to the values you store: keyword for exact string matches, integer or float for ranges, bool for booleans, datetime for timestamps, geo for locations, and text for full-text search. Indexes cost extra memory and compute, so the docs warn against indexing everything — index only the fields your queries actually filter on. In Python the same call reads client.create_payload_index(collection_name="my_docs", field_name="category", field_schema="keyword").

Should I Turn On Quantization?

Quantization trades a little search accuracy for a lot less memory, and Qdrant applies it in the background during optimization. The docs give four methods with honest numbers, summarized here from the official quantization guide (all numbers below are the docs' own):

MethodCompressionWhen to use it
Scalar4xSafe default: float32 to 8-bit ints, usually under 1% accuracy error, and it speeds up comparisons
BinaryUp to 32xHigh-dimensional centered vectors (OpenAI's 1536d ada-002 held 0.98 recall with oversampling); up to 40x search speedup
TurboQuant (4-bit default)8xThe docs' recommended default: scalar-like recall at twice the compression; works with any vector distribution
ProductUp to 64xOnly when memory is the absolute top priority and accuracy/speed matter less

For a beginner, the decision is simple: start with no quantization, and turn it on when your collection no longer fits comfortably in RAM. Binary quantization pairs well with rescoring — the binary pass picks candidates quickly, then Qdrant re-scores the top ones against the original vectors to recover accuracy — and an oversampling factor of 3 is the documented starting point. The docs' own sweep showed rescoring lifting binary accuracy from the mid-70s to the high-90s on high-dimensional models, at the cost of reading the original vectors from disk.

How Does Qdrant Compare to Pinecone and Weaviate?

This is the comparison every team runs before choosing, and the October 2026 landscape has a clear decision map. The key split is deployment model: Qdrant and Weaviate are open source and self-hostable with managed clouds alongside; Pinecone is a fully managed proprietary service — zero servers to run, but you cannot self-host and you pay per usage past the free tier.

QdrantPineconeWeaviate
LicenseApache 2.0ProprietaryBSD-3-Clause
HostingSelf-host or Qdrant CloudManaged only (serverless)Self-host or Weaviate Cloud
Free entry pointFree managed cluster tier + unlimited free self-hostingFree Starter serverless tierFree cloud sandbox + unlimited free self-hosting
Metadata filteringPayload indexes (the rule above)Metadata filtersBuilt-in filters + GraphQL-style query
Hybrid searchBuilt-in hybrid (sparse + dense)Sparse + dense supportBuilt-in BM25 + vector hybrid
Cheapest paid plan (Oct 2026)Usage-based managed tierBuilder, $20/monthFlex, $45/month

The numbers above come from October 2026 comparison roundups that cite vendor pages and release notes (induwara.lk's cited comparison table and tech-insider's October 28, 2026 vector-database roundup, which also notes Qdrant v1.20.0 as the current release line). The decision rule from those roundups: choose Pinecone when zero operational work matters most, Weaviate when you want built-in hybrid search with a GraphQL-native feel, and Qdrant when you want open-source flexibility with strong filtered-search performance you can self-host on your own VPS. If the server question comes up, what is VPS hosting explains the box Qdrant runs happiest on.

How Do I Wire Qdrant into a Real RAG App?

A RAG app splits into two halves: indexing your documents once, and answering questions many times. The indexing half is chunking your documents into passages, embedding each chunk with your chosen model, and upserting each one as a point with the original text in its payload. The answering half is embedding the question with the same model, searching Qdrant for the top-k closest chunks (optionally filtered — for example, only the current user's documents), and passing those chunks to a language model as context.

The smallest working version uses plain Python with sentence-transformers for embeddings and the Qdrant client you already installed:

from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

model = SentenceTransformer("all-MiniLM-L6-v2")  # 384 dimensions
client = QdrantClient(url="http://localhost:6333")

docs = [
    "Qdrant stores vectors and finds the closest ones in milliseconds.",
    "Payload indexes make metadata filtering fast.",
    "Binary quantization cuts memory usage up to 32x.",
]

client.create_collection(
    collection_name="rag_docs",
    vectors_config=VectorParams(size=384, distance=Distance.COSINE),
)

client.upsert(
    collection_name="rag_docs",
    wait=True,
    points=[
        PointStruct(id=i, vector=model.encode(text).tolist(), payload={"text": text})
        for i, text in enumerate(docs)
    ],
)

# Answer half: embed the question, search, return the context chunks
question = "How do I make filtering fast?"
hits = client.query_points(
    collection_name="rag_docs",
    query=model.encode(question).tolist(),
    limit=2,
).points

context = [hit.payload["text"] for hit in hits]
print(context)

Notice the load-bearing detail: the question is embedded with the same model (384 dimensions) as the documents. Mixing models here silently breaks retrieval, because the vectors live in different spaces. When this prototype outgrows a script, the natural next step is a framework: the LangChain beginner guide shows the RAG code that queries Qdrant through its first-party Qdrant vector store, and if you would rather build the app without writing code, the Dify beginner guide covers the no-code app lane that uses vector databases underneath.

What Are the Beginner Mistakes?

Almost every "Qdrant is not working" thread traces back to one of these five:

MistakeWhat happensThe fix
Wrong dimension for the collectionUpserts fail or the collection is unusable for your modelPick the model first (384 for all-MiniLM-L6-v2), then create the collection; mismatches mean recreating it
No volume mount on the Docker runAll collections vanish when the container is removedAlways pass -v "$(pwd)/qdrant_storage:/qdrant/storage:z"
Filtering without a payload indexSlow filtered queries the planner cannot estimatePUT /collections/{name}/index on every filtered field before ingesting
Mixing embedding models in one collectionMeaningless distances, garbage search resultsOne model per collection; re-embed everything if you switch
Using the old search method in new clientsAttribute errors after upgrading qdrant-clientUse query_points (Python) or query (JS); REST uses POST /points/query

Sources

Qdrant official local quickstart (qdrant.tech/documentation/quickstart/ — Docker run command, REST 6333, gRPC 6334, /dashboard web UI, client.query_points API, upsert with wait=True; crawled 2026-10-31 UTC). Qdrant official vectors concepts (qdrant.tech/documentation/concepts/vectors/ — PUT collection creation, 1–65,536 dimension range, sparse vectors, quantization overview; crawled 2026-10-31 UTC). Qdrant official quantization guide (qdrant.tech/documentation/guides/quantization/ — TurboQuant v1.18.0+/4-bit default/bits8 in v1.20.0, scalar 4x with under-1% error, binary up to 32x with up to 40x speedup, ada-002 1536d 0.98 recall@100 with 4x oversampling, 2-bit 16x and 1.5-bit ~21x in v1.15.0, product up to 64x; crawled 2026-10-31 UTC). Qdrant indexing docs via search (qdrant.tech/documentation/manage-data/indexing/ — "index every field you filter on," PUT /collections/{name}/index, field types keyword/integer/float/bool/datetime/uuid/geo/text; fetched 2026-10-31 UTC). Qdrant vector-search-filtering guide via search (qdrant.tech/documentation/search-patterns/vector-search-filtering/ — payload index accelerates filtering, index before ingesting; fetched 2026-10-31 UTC). Qdrant LLM-powered filter automation tutorial via search (sentence-transformers/all-MiniLM-L6-v2, 384 dimensions, create_payload_index Python call; fetched 2026-10-31 UTC). GitHub qdrant/qdrant releases API (v1.20.0 released 2026-10-22; fetched 2026-10-31 UTC). Medium explainer "Qdrant Explained" by mnp975 (Rust, Apache 2.0, point = id + vectors + payload, upsert semantics, "never mix embedding models"; published ~9 hours before writing, fetched 2026-10-31 UTC). Induwara.lk cited vector-database comparison (June 21, 2026 — Qdrant Apache-2.0 self-host + cloud, Pinecone proprietary managed-only with free Starter tier, Weaviate BSD-3-Clause self-host + cloud, free tiers and cheapest paid plans; fetched 2026-10-31 UTC). Tech-insider.org vector database roundup (October 28, 2026 — Qdrant v1.20.0 current release line, Pinecone Builder $20/month, Weaviate Flex $45/month, deployment-model table; fetched 2026-10-31 UTC). LeanOps 2026 comparison (Pinecone fully managed, zero operational work, per-usage cost past free tier; fetched 2026-10-31 UTC).

Qdrant Tutorial FAQ

Is Qdrant free to use?

Yes. Qdrant is open source under the Apache 2.0 license, so you can run it on your own server or laptop for free, including commercially. There is also a managed Qdrant Cloud with a free tier, and the Python client's local in-memory mode is handy for prototyping and tests without any server at all.

How is Qdrant different from Pinecone?

The core difference is deployment model: Pinecone is a fully managed proprietary service — you never run a server, but you cannot self-host — while Qdrant is open source and you can self-host it on your own VPS or use the managed cloud. Qdrant also gives you built-in payload indexes for fast metadata filtering and quantization options that Pinecone's managed model handles differently.

Do I need a GPU to run Qdrant?

No. Qdrant itself runs on CPU — it is a Rust server, and Docker is the usual way to start it. You only need a GPU if the embedding model you run alongside it requires one; the local all-MiniLM-L6-v2 setup in this guide runs on CPU.

What happens if my vector has the wrong dimension?

The collection rejects it: the vector size is fixed when the collection is created and every point must match. There is no in-place migration — create a new collection with the correct size and re-index your data. That is why this guide puts the model-and-dimension decision before the first collection call.

Can Qdrant do keyword search too, or only vector search?

Both. Qdrant supports hybrid search combining dense vectors with sparse keyword vectors, and its payload indexes give you full-text search filters on string fields. The October 2026 release line (v1.20.0) is the current one to pin when you deploy.

Featured on Twelve Tools Launch Llama Tools Launch your MCP