Skip to main content

Qdrant in Docker: a first vector search

This guide takes you from nothing to a working vector similarity search in about ten minutes. You run Qdrant in a Docker container, create a collection, insert a few points by hand, and search them with plain curl. No Python environment, no embedding model, no cleanup anxiety: everything lives in one container and one named volume, and the last section removes both.

Hand-written four-dimensional vectors stand in for real embeddings so every command here is copy-paste-safe. The concepts transfer unchanged when a model produces the vectors instead of you.

Prerequisites

  • Docker Engine or Docker Desktop. Verify with:

    docker --version

    You should see a version string, not "command not found." Any recent version works.

  • curl, which ships with macOS, Linux, and Windows 10+.

  • Ports 6333 and 6334 free on your machine. If another service owns them, adjust the -p flags below and every URL to match.

Run Qdrant

Start the container with a named volume so your data survives restarts:

docker run -d --name qdrant \
-p 6333:6333 -p 6334:6334 \
-v qdrant_storage:/qdrant/storage \
qdrant/qdrant

Port 6333 serves the REST API and dashboard; 6334 serves gRPC, which this guide does not use.

Checkpoint. Ask the server to identify itself:

curl http://localhost:6333/

You should see a small JSON object with a "title" naming qdrant and a "version". If you get connection refused, run docker ps to confirm the container is up, and docker logs qdrant to see why if it is not.

Qdrant also ships a browser dashboard at http://localhost:6333/dashboard, worth opening now so you can watch the next steps land.

Create a collection

A collection is a named set of points that share a vector size and a distance metric. Create one for four-dimensional vectors compared by cosine similarity:

curl -X PUT http://localhost:6333/collections/first_steps \
-H "Content-Type: application/json" \
-d '{ "vectors": { "size": 4, "distance": "Cosine" } }'

Checkpoint. The response should be:

{"result":true,"status":"ok","time":0.1}

(The time value differs run to run. Rerunning the command returns an error that the collection already exists, which is itself a good sign.)

Insert points

A point is an id, a vector, and an optional payload of plain JSON. These four points describe documents, with a kind payload to filter on later. The ?wait=true flag makes the call block until the write is durable, so the checkpoint below is honest:

curl -X PUT "http://localhost:6333/collections/first_steps/points?wait=true" \
-H "Content-Type: application/json" \
-d '{
"points": [
{ "id": 1, "vector": [0.90, 0.10, 0.05, 0.05],
"payload": { "title": "Backup and restore runbook", "kind": "runbook" } },
{ "id": 2, "vector": [0.85, 0.15, 0.10, 0.05],
"payload": { "title": "Disaster recovery runbook", "kind": "runbook" } },
{ "id": 3, "vector": [0.10, 0.90, 0.10, 0.05],
"payload": { "title": "Balance calibration SOP", "kind": "sop" } },
{ "id": 4, "vector": [0.05, 0.10, 0.90, 0.10],
"payload": { "title": "Data API integration notes", "kind": "notes" } }
]
}'

Checkpoint. The response reports "status": "completed". Confirm the count landed:

curl http://localhost:6333/collections/first_steps

Look for "points_count": 4 in the result.

The two runbook vectors you just inserted point in nearly the same direction; the SOP and the notes point elsewhere. Search with a query vector close to the runbooks:

curl -X POST http://localhost:6333/collections/first_steps/points/search \
-H "Content-Type: application/json" \
-d '{
"vector": [0.88, 0.12, 0.05, 0.06],
"limit": 2,
"with_payload": true
}'

Checkpoint. The top two results should be points 1 and 2, the runbooks, each with a score above 0.99. Cosine scores in Qdrant run up to 1.0, where 1.0 means "same direction." That is vector search doing its one job: nearest meaning first.

Search with a filter

Payloads make the search conditional. Ask for things near the query vector, but only where kind is sop:

curl -X POST http://localhost:6333/collections/first_steps/points/search \
-H "Content-Type: application/json" \
-d '{
"vector": [0.88, 0.12, 0.05, 0.06],
"limit": 2,
"with_payload": true,
"filter": {
"must": [ { "key": "kind", "match": { "value": "sop" } } ]
}
}'

Checkpoint. Exactly one result comes back: point 3, the calibration SOP, despite its lower similarity score. Filters constrain the candidate set; the vector still ranks whatever survives.

Clean up

docker stop qdrant && docker rm qdrant
docker volume rm qdrant_storage

Checkpoint. docker ps -a no longer lists the container, and docker volume ls no longer lists qdrant_storage.

Troubleshooting

SymptomLikely causeFix
port is already allocatedAnother service owns 6333 or 6334Change the left side of the -p mappings, and every URL in this guide, to a free port
The container name "/qdrant" is already in useA previous run left a container behinddocker rm -f qdrant, then rerun the docker run command
connection refused on every curlContainer not running, or Docker daemon stoppeddocker ps, then docker logs qdrant
Search returns fewer results than limitSmall collections are smallExpected with 4 points; limit is a maximum, not a promise

Where to go next

  • Replace the hand-written vectors with real embeddings from a sentence-transformer model, keeping every other call identical (only size changes).
  • Move from raw REST to the official Python or JavaScript client libraries.
  • Read the Qdrant documentation for quantization, snapshots, and running beyond a single node.