Milvus Integration#
Dify's Milvus integration is implemented as a plugin in milvus_vector.py, backed by pymilvus.MilvusClient. It supports dense vector search (HNSW index, inner-product metric), hybrid dense+sparse search (BM25, requires Milvus β₯ 2.5.0), token- and username/password-based auth, and one-way TLS.
Configuration is declared in MilvusConfig (Pydantic BaseSettings), which is instantiated by MilvusVectorFactory.init_vector(). All settings are read from environment variables.
Environment Variables#
| Variable | Default | Purpose |
|---|---|---|
MILVUS_URI | http://127.0.0.1:19530 | Server URI |
MILVUS_TOKEN | None | Token-based auth |
MILVUS_USER | None | Username for user/password auth |
MILVUS_PASSWORD | None | Password for user/password auth |
MILVUS_DATABASE | default | Database name |
MILVUS_ENABLE_HYBRID_SEARCH | True | Enable BM25 hybrid search (requires Milvus β₯ 2.5.0) |
MILVUS_ANALYZER_PARAMS | None | Tokenizer params, e.g. {"type": "chinese"} |
MILVUS_SECURE | False | Enable one-way TLS |
MILVUS_SERVER_PEM_PATH | None | Container path to server PEM certificate |
MILVUS_SERVER_NAME | None | TLS SNI / certificate CN or SAN |
Auth validation: MilvusConfig requires either MILVUS_TOKEN or both MILVUS_USER and MILVUS_PASSWORD .
One-Way TLS#
Added in PR #36265 (merged 2026-05-27), one-way TLS maps directly to pymilvus's MilvusClient(secure=True, server_pem_path=..., server_name=...).
The logic in _init_client() only injects TLS kwargs when MILVUS_SECURE=True, so existing deployments are unaffected by default:
MILVUS_SECURE=True
MILVUS_SERVER_PEM_PATH=/etc/milvus/certs/server.pem # must be mounted into container
MILVUS_SERVER_NAME=milvus.example.com # CN or SAN on the server cert
MILVUS_SERVER_PEM_PATH must point to a file inside the container. In Kubernetes, mount the PEM via a Secret volume. In Docker Compose, use a bind mount. MILVUS_SERVER_NAME is required when MILVUS_SERVER_PEM_PATH is set; it is used for SNI and certificate verification .
Hybrid Search#
When MILVUS_ENABLE_HYBRID_SEARCH=True (the default), Dify checks the connected Milvus version at startup via _check_hybrid_search_support(): it requires Milvus β₯ 2.5.0 or Zilliz Cloud. On version check failure, hybrid search is silently disabled.
New collections include a sparse_vector field (BM25 function over content) alongside the dense vector field . Collections created before hybrid search was enabled are missing the sparse_vector field and fall back gracefully β full-text search returns an empty result with a warning rather than an error . To use hybrid search on an existing collection, drop and recreate it.
MILVUS_ANALYZER_PARAMS lets you override the tokenizer per collection (e.g. {"type": "chinese"} for Chinese text) .
Upgrade & Migration#
Re-indexing with flask vdb-migrate#
flask vdb-migrate re-vectorizes all knowledge-base and annotation data into the currently configured VECTOR_STORE. Use it when switching vector store backends or when vectorization must be rebuilt.
Milvus is classified as an upper_collection_vector_types backend , meaning collection names are generated uppercase via Dataset.gen_collection_name_by_id(). Run with --scope knowledge, --scope annotation, or (default) --scope all.
docker compose exec api flask vdb-migrate --scope all
Standard Dify Upgrade Sequence#
When upgrading Dify versions, always run schema migration before data commands:
docker compose exec api flask upgrade-dbβ applies Alembic schema migrationsdocker compose exec api flask backfill-plugin-auto-upgradeβ backfills plugin strategy rows; must follow step 1 β thecategorycolumn it queries does not exist until after the schema migration runsdocker compose exec api flask vdb-migrateβ only needed if re-indexing vector data is required
Key Source Files#
| File | Purpose |
|---|---|
api/configs/middleware/vdb/milvus_config.py | All Milvus env var definitions |
api/providers/vdb/vdb-milvus/src/dify_vdb_milvus/milvus_vector.py | Client init, collection schema, search logic |
api/commands/vector.py | flask vdb-migrate implementation |