Pourquoi auto-héberger Qdrant sur un VPS
Qdrant stocke et interroge des vecteurs d'embeddings pour trouver les éléments les plus proches sémantiquement : c'est la brique de recherche au cœur des chatbots RAG, des moteurs de recommandation et de la recherche en langage naturel. Écrite en Rust, elle est rapide et économe en ressources, ce qui la rend idéale à héberger sur un VPS plutôt que de payer un service vectoriel managé facturé au vecteur stocké.
L'auto-hébergement est essentiel pour les applications IA traitant des données sensibles : vos embeddings, souvent dérivés de documents internes confidentiels, ne quittent jamais votre infrastructure. Vous maîtrisez aussi la latence, déterminante quand chaque requête utilisateur déclenche une recherche vectorielle, et vous évitez les limites de débit des API tierces lors des montées en charge.
Pré-requis et ressources VPS recommandées
- RAM : minimum 1 Go pour tester, 2 Go pour quelques centaines de milliers de vecteurs, 4–8 Go au-delà du million de vecteurs en haute dimension (l'index HNSW vit en mémoire).
- Stockage : 20–50 Go de SSD NVMe pour persister les collections et les snapshots.
- CPU : 1–2 vCPU suffisent pour un trafic modéré ; ajoutez des cœurs si vous indexez en continu.
- Docker et Docker Compose installés sur le VPS.
- Port 6333 (REST) et 6334 (gRPC) accessibles depuis votre application ; 80/443 pour le reverse proxy public.
- Un enregistrement DNS A (ex.
vectors.mondomaine.com) si vous exposez l'API en HTTPS.
Installer Qdrant avec Docker Compose
Créer le répertoire et le fichier compose.yml
Sur le VPS, créez un dossier dédié :
mkdir -p /opt/qdrant && cd /opt/qdrant. Créez le fichiercompose.ymlsuivant :services: qdrant: image: qdrant/qdrant:latest restart: unless-stopped ports: - "127.0.0.1:6333:6333" - "127.0.0.1:6334:6334" volumes: - ./qdrant_storage:/qdrant/storage environment: - QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY}Lier les ports à
127.0.0.1empêche toute exposition directe sur Internet ; seul le reverse proxy y accède.Générer la clé API et démarrer le conteneur
Créez le fichier
.envavec votre clé :echo "QDRANT_API_KEY=$(openssl rand -hex 32)" > .envDémarrez Qdrant :
docker compose up -dVérifier la santé de l'instance
Attendez quelques secondes puis vérifiez l'endpoint de santé :
curl -s http://localhost:6333/healthz # Attendu : {"title":"qdrant - vector search engine","version":"..."}Créer votre première collection
Créez une collection de test avec des vecteurs de dimension 384 (format typique de
all-MiniLM-L6-v2) :curl -s -X PUT http://localhost:6333/collections/test \ -H 'Content-Type: application/json' \ -H "api-key: $(grep QDRANT_API_KEY .env | cut -d= -f2)" \ -d '{"vectors":{"size":384,"distance":"Cosine"}}'
Sécuriser l'accès : clé API et TLS
Qdrant n'active aucune authentification par défaut : une instance exposée sans clé API permettrait à n'importe qui de lire, modifier ou supprimer toutes vos collections. La variable d'environnement QDRANT__SERVICE__API_KEY est la première ligne de défense ; le reverse proxy avec TLS est la seconde.
Pour Caddy, la configuration minimale est :
vectors.mondomaine.com {
reverse_proxy localhost:6333
}Caddy obtient et renouvelle automatiquement le certificat Let's Encrypt. Pour nginx, ajoutez dans votre bloc server :
location / {
proxy_pass http://127.0.0.1:6333;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}Activez TLS avec Certbot : certbot --nginx -d vectors.mondomaine.com.
Tester l'authentification et le reverse proxy
Vérifier que l'accès sans clé est refusé
curl -s https://vectors.mondomaine.com/collections # Attendu : {"status":{"error":"Unauthorized",...}}Appel authentifié avec la clé API
curl -s https://vectors.mondomaine.com/collections \ -H "api-key: VOTRE_CLE" # Attendu : {"result":{"collections":[]},"status":"ok",...}Vérifier le certificat TLS
curl -sv https://vectors.mondomaine.com/healthz 2>&1 | grep 'SSL connection\|issuer' # Attendu : lignes confirmant Let's Encrypt et TLS 1.3
Créer une collection et insérer des vecteurs avec le client Python v1
Installer le client officiel
pip install qdrant-client>=1.7Connexion avec clé API
from qdrant_client import QdrantClient client = QdrantClient( url="https://vectors.mondomaine.com", api_key="VOTRE_CLE", )Créer une collection avec VectorParams
from qdrant_client.models import Distance, VectorParams client.recreate_collection( collection_name="documents", vectors_config=VectorParams(size=384, distance=Distance.COSINE), )sizedoit correspondre à la dimension des embeddings de votre modèle.Distance.COSINEconvient pour la majorité des modèles de phrase (sentence transformers).Insérer des points avec payload et interroger
from qdrant_client.models import PointStruct # Insérer des vecteurs client.upsert( collection_name="documents", points=[ PointStruct( id=1, vector=[0.1, 0.2, ...], # vecteur de taille 384 payload={"title": "Guide Qdrant", "lang": "fr"}, ), ], ) # Rechercher les 5 plus proches voisins results = client.search( collection_name="documents", query_vector=[0.1, 0.21, ...], limit=5, with_payload=True, ) for r in results: print(r.score, r.payload)
Optimiser les performances : tuning HNSW
L'algorithme HNSW (Hierarchical Navigable Small World) est le cœur de la recherche rapide dans Qdrant. Trois paramètres gouvernent le compromis vitesse/précision/mémoire.
Paramètres HNSW clés
Faites défiler le tableau
| Paramètre | Valeur par défaut | Plage recommandée | Impact |
|---|---|---|---|
| m | 16 | 16–64 | Connexions par nœud — plus élevé = recall supérieur, plus de RAM |
| ef_construct | 100 | 100–400 | Qualité de la construction de l'index — plus élevé = index de qualité supérieure, construction plus lente |
| ef | 128 (à la requête) | 64–512 | Qualité de la recherche — plus élevé = recall supérieur, requête plus lente |
Configurer HNSW à la création de la collection
Passez les paramètres HNSW directement dans recreate_collection :
from qdrant_client.models import HnswConfigDiff
client.recreate_collection(
collection_name="documents",
vectors_config=VectorParams(size=384, distance=Distance.COSINE),
hnsw_config=HnswConfigDiff(m=32, ef_construct=200),
)Pour une recherche équilibrée sur un corpus de 500 000 vecteurs, m=32 et ef_construct=200 offrent un bon recall (>0,97) sans surcharger la RAM. Augmentez ef à la requête pour les cas où la précision prime sur la latence.
Réduire l'empreinte mémoire : quantization scalaire et binaire
- Quantization scalaire (uint8) : compresse chaque coordonnée float32 (4 octets) en uint8 (1 octet), soit une réduction de RAM d'environ 4×. Légère perte de précision compensée par l'oversampling. Activez-la avec
ScalarQuantizationConfig(type=ScalarType.INT8, quantile=0.99, always_ram=True). - Quantization binaire : va jusqu'à 32× de réduction en binarisant chaque dimension. Idéale pour des modèles entraînés pour la quantization binaire (ex. Cohere
embed-english-v3.0). Plus rapide mais recall plus faible sans oversampling. - Paramètre
on_disk: stockez les vecteurs originaux sur disque (on_disk=True) et ne gardez en RAM que l'index quantifié. Indispensable sur un VPS à mémoire contrainte. - Oversampling : avec
oversampling=2.0à la requête, Qdrant récupère 2× plus de candidats depuis l'index quantifié puis les rerange avec les vecteurs originaux — recall proche du full-precision pour un coût modéré.
Sauvegarder et restaurer avec les snapshots Qdrant
Créer un snapshot d'une collection
curl -s -X POST https://vectors.mondomaine.com/collections/documents/snapshots \ -H "api-key: VOTRE_CLE" # Retourne le nom du snapshot : {"result":{"name":"documents-...-...","creation_time":"..."}}Lister les snapshots disponibles
curl -s https://vectors.mondomaine.com/collections/documents/snapshots \ -H "api-key: VOTRE_CLE"Télécharger le snapshot
curl -O https://vectors.mondomaine.com/collections/documents/snapshots/NOM_DU_SNAPSHOT \ -H "api-key: VOTRE_CLE"Copiez ensuite le fichier
.snapshotvers un stockage objet (ex. Scaleway Object Storage, Backblaze B2) pour une sauvegarde hors site.Restaurer depuis un snapshot
curl -s -X PUT https://vectors.mondomaine.com/collections/documents/snapshots/upload \ -H "api-key: VOTRE_CLE" \ -H 'Content-Type: multipart/form-data' \ -F [email protected]La collection est recréée à partir du snapshot avec toutes ses données et sa configuration HNSW d'origine.
Monitoring avec Prometheus et Grafana
Qdrant expose un endpoint /metrics au format Prometheus sur le port 6333. Les métriques utiles pour la production : app_requests_total (débit), app_requests_duration_seconds (latence p50/p99), collection_vector_count (taille du corpus) et les erreurs gRPC.
Ajoutez la cible dans votre prometheus.yml :
scrape_configs:
- job_name: qdrant
static_configs:
- targets: ['localhost:6333']
metrics_path: /metrics
bearer_token: VOTRE_CLESous Grafana, importez le dashboard communautaire Qdrant (ID 18278) pour visualiser la latence p99 et le taux d'erreur en temps réel.
Dépannage courant
- Connection refused sur le port 6333 : vérifiez que le conteneur tourne (
docker compose ps) et que le port n'est pas bloqué parufw—ufw allow from 127.0.0.1 to any port 6333. Si le port est lié à0.0.0.0au lieu de127.0.0.1, corrigez lecompose.ymlet relancez. - Erreur OOM (Out of Memory) / conteneur tué : réduisez
ef_construct(ex. 100 → 64) ou activez la quantization scalaire pour diminuer l'empreinte RAM de l'index. Activezon_disk: truepour les vecteurs originaux. Vérifiez la consommation réelle avecdocker stats qdrant. - Lenteur au démarrage ou au premier chargement : l'index HNSW est chargé depuis le disque au premier accès. Utilisez un SSD NVMe (pas un disque rotatif) et préchauffez la collection après démarrage avec une requête de recherche vide.
- Unauthorized 403 : la clé API passée dans le header
api-keyne correspond pas àQDRANT__SERVICE__API_KEY. Vérifiez l'absence d'espaces ou de sauts de ligne dans la variable d'environnement. - Recall dégradé après activation de la quantization binaire : augmentez
oversamplingà3.0ou4.0et activezrescore=Truepour reranker les candidats avec les vecteurs originaux.
Associer Qdrant à Ollama pour une stack RAG entièrement autonome
Faites tourner Ollama sur le même VPS pour la génération des embeddings (ollama pull mxbai-embed-large) et l'inférence (ollama pull llama3.1:8b). Votre pipeline RAG fonctionne alors de bout en bout sans aucun appel d'API externe : question de l'utilisateur → embedding Ollama → recherche Qdrant → fragments top-k → génération Ollama → réponse fondée sur les faits.
Ajoutez LiteLLM en frontal pour exposer à votre application un endpoint unifié compatible avec l'API OpenAI — et changez de modèle sans modifier une seule ligne de code. Un VPS avec 8 Go de RAM héberge confortablement Qdrant + Ollama (modèle 7B quantifié) + LiteLLM pour un corpus de quelques centaines de milliers de vecteurs.