Qdrant Deployment (Air-Gapped) — Vector DB for assistant-service¶
Qdrant is the vector database assistant-service's intelligence layer uses for the memory / mem0 feature (long-term agent memory: stores text embeddings, searches by similarity). It is not a subchart — it's an independent service referenced by URL.
OPTIONAL — the air-gap deployment runs WITHOUT Qdrant
The qdrant and memory_ingestion config blocks are optional (| None in the
schema). When both are omitted, get_memory_ingestion_service() returns None and the memory
feature is simply off — assistant-service starts and runs fine without Qdrant. (The on-prem
production reference deploys this way.)
For air-gap bring-up: do NOT deploy Qdrant. Omit the […intelligence.qdrant] and
[…intelligence.memory_ingestion] TOML blocks, and drop QDRANT_SECRET / QDRANT_BASIC_PASSWORD
from the secret seed. Only follow the steps below if you specifically want the long-term memory
/ mem0 feature.
What assistant-service expects (only when memory IS enabled)
From intelligence.qdrant in the config:
[<env>.intelligence.qdrant]
url = "https://qdrant.ask.mod.auh1.dev.dir"
basic_user = "qdrant"
api_key = "@format {env[QDRANT_SECRET]}" # Qdrant API key
basic_password = "@format {env[QDRANT_BASIC_PASSWORD]}" # ingress basic-auth (optional)
[<env>.intelligence.memory_ingestion]
embedding_model_name = "BAAI/bge-m3"
embedding_model_dims = 1024
collection_name = "memories_bge_m3"
Check whether it already exists¶
Empty → build it (below).Prerequisites¶
- [x] Image mirrored:
harbor.cl1.sq4.aegis.internal/dockerhub/qdrant/qdrant:v1.12.5(added toscripts/harbor-mirror/mirror-config.yaml) - [x] Ceph RBD storage class (
csi-rbd-sc) for the collection volume - [x] cert-manager
internal-ca-issuerfor the ingress TLS - [x] Vault key
QDRANT_SECRET(API key) seeded; optionalQDRANT_BASIC_PASSWORD
Manifests (ArgoCD app — ask namespace)¶
A StatefulSet is enough (single node; Qdrant clustering is optional). Add these under
mod-ask-devops/applications/qdrant/ and wire an Application like the others.
1. Secret (API key) — via ESO from Vault:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: qdrant-secret, namespace: ask }
spec:
refreshInterval: 1m
secretStoreRef: { kind: ClusterSecretStore, name: vault-backend }
target: { name: qdrant-secret, creationPolicy: Owner }
data:
- secretKey: api-key
remoteRef: { key: ask/assistant-service/auh1-dev, property: QDRANT_SECRET }
2. StatefulSet + Service:
apiVersion: apps/v1
kind: StatefulSet
metadata: { name: qdrant, namespace: ask }
spec:
serviceName: qdrant
replicas: 1
selector: { matchLabels: { app: qdrant } }
template:
metadata: { labels: { app: qdrant } }
spec:
nodeSelector: { node.kubernetes.io/role: ask71 }
imagePullSecrets: [{ name: harbor-pull-secret }]
containers:
- name: qdrant
image: harbor.cl1.sq4.aegis.internal/dockerhub/qdrant/qdrant:v1.12.5
ports: [{ name: http, containerPort: 6333 }, { name: grpc, containerPort: 6334 }]
env:
- name: QDRANT__SERVICE__API_KEY
valueFrom: { secretKeyRef: { name: qdrant-secret, key: api-key } }
volumeMounts: [{ name: storage, mountPath: /qdrant/storage }]
readinessProbe: { httpGet: { path: /readyz, port: http }, initialDelaySeconds: 10 }
livenessProbe: { httpGet: { path: /livez, port: http }, initialDelaySeconds: 20 }
resources:
requests: { cpu: 500m, memory: 2Gi }
limits: { memory: 4Gi }
volumeClaimTemplates:
- metadata: { name: storage }
spec:
accessModes: [ReadWriteOnce]
storageClassName: csi-rbd-sc
resources: { requests: { storage: 20Gi } }
---
apiVersion: v1
kind: Service
metadata: { name: qdrant, namespace: ask }
spec:
selector: { app: qdrant }
ports: [{ name: http, port: 6333 }, { name: grpc, port: 6334 }]
3. Ingress (so url = https://qdrant.ask.mod.auh1.dev.dir resolves; API-key auth is in-app):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: qdrant
namespace: ask
annotations:
cert-manager.io/cluster-issuer: internal-ca-issuer
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls: [{ secretName: qdrant-tls, hosts: [qdrant.ask.mod.auh1.dev.dir] }]
rules:
- host: qdrant.ask.mod.auh1.dev.dir
http: { paths: [{ path: /, pathType: Prefix, backend: { service: { name: qdrant, port: { number: 6333 } } } }] }
In-cluster vs ingress URL
assistant-service runs in-cluster, so it can use http://qdrant.ask.svc.cluster.local:6333
directly (no TLS, no ingress) — simpler and avoids the internal-CA trust dance. Use the ingress
only if you need external/admin access. Set intelligence.qdrant.url accordingly.
Validate¶
# pod ready
kubectl get pods -n ask -l app=qdrant
# API reachable with the key
kubectl exec -n ask qdrant-0 -- sh -c 'wget -qO- --header="api-key: $QDRANT__SERVICE__API_KEY" http://localhost:6333/collections'
{"result":{"collections":[...]},"status":"ok"}. After assistant-service runs, the
memories_bge_m3 collection appears here.
Production / air-gap notes¶
- Single-node is fine for dev; for HA, Qdrant supports clustering (raft) — needs ≥3 replicas + a headless service.
- Back up
/qdrant/storage(snapshots API or volume snapshots) — it holds all embeddings. - The embedding model (
BAAI/bge-m3, 1024-dim) must be served by Foundry/vLLM — Qdrant only stores the vectors; assistant-service computes them via Foundry. See Foundry.