Concepts in one table
| Term | Meaning |
|---|---|
| Peer | One Qdrant node in the cluster (Raft consensus between peers) |
| Shard | A slice of a collection's points; each lives on one or more peers |
| Replica | A copy of a shard on another peer |
shard_number | Shards per collection; fixed at creation on self-hosted |
replication_factor | Copies of each shard |
write_consistency_factor | Replicas that must confirm a write before it succeeds |
Collection metadata (create, delete, shard placement) goes through Raft consensus. Point writes go directly to the replicas of the relevant shard.
Step 1: enable cluster mode
In config.yaml (or env QDRANT__CLUSTER__ENABLED=true):
cluster:
enabled: true
p2p:
port: 6335
Start the first peer with its own URI, and the others pointing at it:
# first peer
qdrant --uri http://qdrant-0.qdrant-headless:6335
# every other peer
qdrant --bootstrap http://qdrant-0.qdrant-headless:6335 --uri http://qdrant-1.qdrant-headless:6335
On Kubernetes the Qdrant Helm chart does this for you with a StatefulSet and headless service. Each peer needs its own persistent volume.
Check the cluster:
curl -s -H "api-key: $QDRANT_API_KEY" http://qdrant:6333/cluster | jq
You should see every peer, one Raft leader, and no pending operations.
Note
Raft needs a majority of peers. Run 3 or more peers for a cluster that survives a node failure. With 2 peers, losing one stops collection-level changes.
Step 2: choose shard and replica counts
Shard count can't be changed later on self-hosted Qdrant (resharding is a Qdrant Cloud feature); changing it means creating a new collection and re-uploading. So plan for growth:
| Guideline | Why |
|---|---|
| Shards ≥ 2 × expected max peers you'll grow to soon | Shards can be moved to new peers; you can't split them |
| A number with many divisors (6, 12) | Spreads evenly over 2, 3, 4, 6 peers |
replication_factor: 2 minimum for production | Survives one peer loss |
write_consistency_factor ≤ replication factor | Higher = safer writes, more latency, more write failures when a replica is down |
curl -X PUT http://qdrant:6333/collections/kb_chunks \
-H "api-key: $QDRANT_API_KEY" -H "Content-Type: application/json" \
-d '{
"vectors": {"size": 1536, "distance": "Cosine"},
"shard_number": 6,
"replication_factor": 2,
"write_consistency_factor": 1
}'
Step 3: check where shards are
curl -s -H "api-key: $QDRANT_API_KEY" http://qdrant:6333/collections/kb_chunks/cluster | jq
The response lists local_shards and remote_shards with peer IDs and state (Active, Partial, Dead…), plus any shard_transfers in progress.
Step 4: add a node and move data onto it
A new peer joins empty; data isn't rebalanced automatically on self-hosted. Move shards explicitly:
curl -X POST http://qdrant:6333/collections/kb_chunks/cluster \
-H "api-key: $QDRANT_API_KEY" -H "Content-Type: application/json" \
-d '{
"move_shard": {
"shard_id": 4,
"from_peer_id": 381894127,
"to_peer_id": 467122995,
"method": "stream_records"
}
}'
Transfer methods:
| Method | Behaviour |
|---|---|
stream_records (default) | Streams points in batches; the target rebuilds the index |
snapshot | Sends a shard snapshot including the index; faster for big shards, more temporary disk |
wal_delta | Sends only missing operations; for catching up a replica that was briefly offline (newer versions) |
Watch progress in the shard_transfers section of the collection cluster info. Move one or two shards at a time; transfers use network, disk and CPU on both peers.
To add a replica instead of moving:
-d '{"replicate_shard": {"shard_id": 4, "from_peer_id": 381894127, "to_peer_id": 467122995}}'
To remove a replica from a peer:
-d '{"drop_replica": {"shard_id": 4, "peer_id": 381894127}}'
Never drop the last active replica of a shard.
Step 5: reads and consistency
Reads can ask for stronger consistency per request:
curl -X POST "http://qdrant:6333/collections/kb_chunks/points/query?consistency=majority" ...
Values: a number of replicas, majority, quorum or all. Default reads from one replica, which is fastest but can briefly return stale data right after a write.
Step 6: when a peer fails
| Situation | What happens | Action |
|---|---|---|
| Peer down briefly | Its replicas marked dead/partial; other replicas serve | Restart; Qdrant recovers the replica via transfer |
| Peer lost permanently | Shards with replicas elsewhere keep working | Replicate those shards to another peer, then remove the dead peer |
| Shard had only one replica on the lost peer | Shard data unavailable | Restore from snapshot; this is why replication factor 2+ matters |
Remove a dead peer once its shards are re-replicated:
curl -X DELETE "http://qdrant:6333/cluster/peer/381894127" -H "api-key: $QDRANT_API_KEY"
The call refuses to remove a peer that still holds shards unless forced; don't force it unless you've confirmed the data exists elsewhere.
Step 7: Kubernetes specifics
- Use a PodDisruptionBudget (
maxUnavailable: 1) so node drains don't take down several peers. - Spread peers across zones with topology spread constraints, and remember a replica on the same zone doesn't protect against a zone outage.
- Don't scale the StatefulSet down without first moving shards off the last peer; its PVC holds data.
Checklist
| Item | Check |
|---|---|
| Peers | 3+; all in /cluster; no pending operations |
| Shards | Count planned for growth |
| Replication | Factor 2+ for production collections |
| Placement | Replicas of the same shard on different zones |
| Scale-out | Shard moves done and verified after adding peers |
Prevent problems next time
- Decide shard counts with a 2–3 year growth estimate; recreating collections later is expensive.
- Script shard moves and test them in staging.
- Alert on dead or partial replicas from
/collections/{name}/cluster. - Take collection snapshots before cluster changes.