The live Qdrant servers of database-compose.yml, the one-shots that seed them and generate their keys, and the scripts those one-shots run.
The vector family's captures (tests/fixtures/vector/), the Qdrant provider's captures and live check, and every hand pass run against these servers, so each seeded collection below exists for a rule and says which.
| Service | Profile | From the host | Transport and authentication |
|---|---|---|---|
qdrant |
none | 127.0.0.1:6333 |
Plaintext, no key |
qdrant-auth |
qdrant-auth |
127.0.0.1:6343 |
Plaintext, an admin key, a read-only key and JWT RBAC |
qdrant-tls |
qdrant-tls |
127.0.0.1:6353 |
TLS on REST, an admin key and a read-only key |
qdrant-mtls |
qdrant-tls |
127.0.0.1:6363 |
TLS that verifies the client's certificate, an admin key and a read-only key |
The one-shots are qdrant-seed, which seeds qdrant and, run by hand, qdrant-auth, and qdrant-keys, which generates the keyed servers' keys, configuration and certificates.
Every server runs ghcr.io/qdrant/qdrant/qdrant:v1.19.1, the GHCR mirror, pinned by the digest sha256:808d42530f48a2b88abe960165ffe81e9ec71f505d72e6404145444e0e085822; it reports commit 6ab21cac.
The one-shots run python:3.14-slim and alpine/openssl:3.5.8, each pinned by digest in the compose file.
Each keyed server reads its keys and TLS settings from the local.yaml that qdrant-keys generated, copied into place when the container starts, never from an environment variable in the compose file.
Every server sets QDRANT__TELEMETRY_DISABLED, because a server otherwise reports to telemetry.qdrant.io every hour.
No server publishes gRPC 6334 or the internal port 6335, which the provider never dials.
No server has a data volume: removing a container resets it, and the seed fills it again.
Every port is bound to a loopback address only, and each server is bounded to 2 CPUs and 2 GiB, against 44 to 80 MiB measured after seeding.
qdrant-auth names http://host.docker.internal:18904/infer as its inference address, host.docker.internal mapped to the host's gateway.
A live check runs a listener there, bound to the bridge gateway address alone, and the listener must receive nothing.
Name the compose project and every service, as every command here does.
up with no service names starts every server in database-compose.yml, and the project name keeps one set of volumes when a command runs from a git worktree.
Start a server with up --wait, which returns once it is healthy, and a one-shot with a plain up -d, then read its exit code with docker wait, which must print 0.
docker compose -p libredb-studio -f database-compose.yml up -d --wait qdrant
docker compose -p libredb-studio -f database-compose.yml up -d qdrant-seed
docker wait libredb-qdrant-seed
docker compose -p libredb-studio -f database-compose.yml --profile qdrant-auth --profile qdrant-tls up -d qdrant-keys
docker wait libredb-qdrant-keys
docker compose -p libredb-studio -f database-compose.yml --profile qdrant-auth --profile qdrant-tls up -d --wait \
qdrant-auth qdrant-tls qdrant-mtls
docker compose -p libredb-studio -f database-compose.yml run --rm --no-deps -T qdrant-seed \
--url http://qdrant-auth:6333 --api-key-file /keys/auth/admin.keyThe first seed installs its pinned packages and takes about a minute; a second run on a seeded server creates nothing. To read a seeded server back without writing to it, and to print the manifest the vector fixtures record:
docker compose -p libredb-studio -f database-compose.yml run --rm --no-deps -T qdrant-seed \
--url http://qdrant:6333 --verify
docker compose -p libredb-studio -f database-compose.yml run --rm --no-deps -T qdrant-seed \
--url http://qdrant:6333 --manifest --image "$(docker inspect --format '{{.Config.Image}}' libredb-qdrant)"--manifest requires --image, the server's reference pinned by digest, and records that image, its digest and the date it printed the manifest.
To start the keyed fixtures over, remove their containers by name, then the keys volume, whose name carries the project's prefix:
docker compose -p libredb-studio -f database-compose.yml --profile qdrant-auth --profile qdrant-tls rm -s -f \
qdrant-keys qdrant-auth qdrant-tls qdrant-mtls
docker volume rm libredb-studio_qdrant-keysseed.pycreates every collection below that is missing and leaves what exists, so a second run does no work;--verifyreads every collection back and exits 1 on a difference, and--manifestprints, as JSON, each collection's vectors, its point count and its first five points with every vector the server returns as the seed sent it. The data is deterministic, from the fixed numpy seeds 20261002, 7, 11 and 20261003, uuid5 ids of a fixed namespace and fixed timestamps, so every point is byte-identical across servers and resets.keys.sh <directory>generates every key,local.yamland certificate into theqdrant-keysvolume at first start, so no key and no private key is ever committed.requirements.inpins every package the seed installs, the versions the research seed's environment held: qdrant-client 1.19.1, numpy 2.5.3 and their dependencies.requirements.txtadds the sha256 of each one'slinux/amd64wheel; it is generated bysh docker/lock-requirements.sh docker/qdrant <the seed's image>and never edited by hand. Soqdrant-seeddeclaresplatform: linux/amd64, and an arm64 host runs it under emulation instead of failing on the hashes.
Files in the qdrant-keys volume |
What they are |
|---|---|
auth/admin.key, auth/read-only.key, auth/local.yaml |
qdrant-auth's keys, and its configuration with jwt_rbac: true |
tls/admin.key, tls/read-only.key, tls/local.yaml |
qdrant-tls's keys, and its configuration with enable_tls: true |
mtls/admin.key, mtls/read-only.key, mtls/local.yaml |
qdrant-mtls's keys, and its configuration with verify_https_client_certificate: true |
ca.pem, server.pem, server.key |
the CA and the certificate both TLS servers present, naming localhost, 127.0.0.1, qdrant-tls and qdrant-mtls |
client.pem, client.key |
the client certificate qdrant-mtls accepts |
Each key is 32 random bytes written as 64 hexadecimal characters, above the 32 bytes the server asks of the key it signs JWTs with.
A live check mints every JWT it needs at test time, HS256 with auth/admin.key, and never writes one into the repository.
Send a key or a JWT in the api-key header or as Authorization: Bearer.
Copy the volume out of the one-shot's container into a fresh directory only you can read, outside the repository, so the copy never lands in an earlier one:
dir=$(mktemp -d)
docker cp libredb-qdrant-keys:/keys "$dir/keys"| Collection | What it holds | The rule it serves |
|---|---|---|
docs |
2,000 points: the edge ids 0, 42, 2^53 + 1, 2^63 and 2^64 - 1, 995 ids at 2^60 + n and 1,000 UUIDs; named vectors text (384, Cosine), image (64, Euclid, absent on one point in ten) and colbert (16, Dot, a multivector of one to three rows), a sparse keywords with idf; eleven payload indexes; heterogeneous payload fields |
ids above 2^53 and UUIDs side by side, every named vector kind, payload indexes |
small_dtypes |
200 points: float16, uint8 and turbo4 vectors, a Manhattan vector and a sparse vector with a uint8 index |
every datatype and distance the v1 cells render |
plain |
300 points with one unnamed 4-dimension Dot vector and a city payload |
the shape of the documentation's own examples |
scratch |
nothing at seed time; an unnamed 8-dimension vector and a sparse sp |
the shared writable collection |
empty_novec |
a collection with no vector at all | a collection that declares no vector |
edge_values |
three points: float16 at 65504 and 2^-24 and one element of 70000 that overflows to null, uint8 at 0 and 255, float32 at its range ends, a two-row integral multivector, a sparse index 4294967295, a sparse value of 3.4e38, and (3,4) |
the copy loop's edge rows; the score null Qdrant prints for the 3.4e38 point's self-search; (3,4) against the origin answering 5.0 with Euclid |
payload_spread |
20,000 points with a 4-dimension vector: variant an integer on the first 10,000 ids and a string after them, a price whose every fourth value is integral and written as JavaScript writes one, and keys present on 2, 1 and 0.1 percent of the points |
the payload sample's rules |
The aliases docs_alias and plain_alias name docs and plain.