Milvus for Enterprise Vector Search: Architecture, Deployment, and Security Guide
Prerequisites
- Kubernetes and Helm basics
- Python familiarity with PyMilvus
Steps
Milvus is an open-source vector database built for high-scale similarity search across embeddings used in RAG, semantic search, recommendation, and vision workloads. This guide explains its architecture, production deployment patterns, implementation steps, and security hardening for enterprise teams.
Overview
Milvus is a distributed vector database designed to store, index, and search high-dimensional embeddings at scale. Enterprises use it to power retrieval-augmented generation, semantic search, fraud analytics, image similarity, and recommendation systems where low-latency nearest-neighbor search is critical.
Milvus supports approximate nearest neighbor indexing methods such as HNSW, IVF_FLAT, and IVF_PQ, along with scalar filtering and hybrid search. In enterprise environments, teams adopt Milvus because it separates compute and storage, integrates with Kubernetes, and supports cloud object storage for durable, scalable operations.
Architecture
Milvus uses a modular architecture with stateless and stateful services.
Core components
- Proxy: entry point for client requests and request routing.
- RootCoord: manages metadata and collection lifecycle.
- DataCoord: coordinates data ingestion and segment management.
- QueryCoord: schedules query workloads across query nodes.
- IndexCoord: manages index build tasks.
- DataNode / QueryNode / IndexNode: worker nodes for ingest, query, and indexing.
- etcd: metadata store and service coordination.
- MinIO or S3: object storage for logs, index files, and segment data.
- Pulsar: message streaming for insert and delete operations.
Deployment models
- Standalone: suitable for development and small test environments.
- Distributed on Kubernetes: preferred for enterprise production.
- Managed Zilliz Cloud: operationally simpler for teams that want Milvus-compatible managed service.
Data flow
- Client sends inserts or search requests to Proxy.
- Inserts are written through message queues and persisted to object storage.
- Data nodes flush segments and trigger index creation.
- Query nodes load segments and indexes into memory for search.
- Search combines vector similarity with optional scalar filters.
Implementation Guide
The example below deploys Milvus distributed on Kubernetes using Helm.
- Add the Milvus Helm repository.
helm repo add milvus https://zilliztech.github.io/milvus-helm/
helm repo update
kubectl create namespace milvus
- Create a production values file.
cluster:
enabled: true
etcd:
replicaCount: 3
pulsar:
enabled: true
bookkeeper:
replicaCount: 3
minio:
mode: distributed
replicas: 4
standalone:
enabled: false
proxy:
replicas: 2
queryNode:
replicas: 3
dataNode:
replicas: 3
indexNode:
replicas: 2
- Install Milvus.
helm install milvus milvus/milvus -n milvus -f values-prod.yaml
kubectl get pods -n milvus
- Expose the Proxy service internally.
kubectl get svc -n milvus
kubectl port-forward -n milvus svc/milvus-proxy 19530:19530
-
Create a collection and index using Python.
-
Validate health and persistence.
kubectl logs -n milvus deploy/milvus-proxy
kubectl logs -n milvus statefulset/milvus-etcd
Code Examples
1. Helm values for production
log:
level: info
common:
security:
authorizationEnabled: true
externalS3:
enabled: true
host: s3.eu-central-1.amazonaws.com
port: 443
accessKey: ${S3_ACCESS_KEY}
secretKey: ${S3_SECRET_KEY}
bucketName: milvus-prod-data
useSSL: true
2. Python collection creation and search
from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection
connections.connect(alias="default", host="127.0.0.1", port="19530")
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=False),
FieldSchema(name="tenant_id", dtype=DataType.VARCHAR, max_length=64),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=384),
]
schema = CollectionSchema(fields, description="enterprise search")
collection = Collection("docs", schema=schema)
collection.create_index("embedding", {"index_type": "HNSW", "metric_type": "COSINE", "params": {"M": 16, "efConstruction": 200}})
collection.load()
results = collection.search(data=[[0.1] * 384], anns_field="embedding", param={"metric_type": "COSINE", "params": {"ef": 64}}, limit=5, expr='tenant_id == "acme"')
print(results)
3. Kubernetes secret for external object storage
kubectl create secret generic milvus-s3 -n milvus \
--from-literal=S3_ACCESS_KEY='AKIAXXXXX' \
--from-literal=S3_SECRET_KEY='xxxxxxxxxxxxxxxx'
Security Hardening
- Enable TLS between clients, ingress, and internal services where supported by your service mesh or ingress controller.
- Use external KMS-backed secrets with Kubernetes Secrets encrypted at rest.
- Restrict network paths using Kubernetes NetworkPolicies so only application namespaces can reach
milvus-proxy. - Enable authorization and isolate tenants using separate collections or databases plus scalar filters.
- Encrypt object storage with SSE-S3 or SSE-KMS for MinIO or S3.
- Audit access through ingress logs, Kubernetes audit logs, and application-level request tracing.
- Pin image versions and scan images before deployment.
Comparison
| Product | Pricing | Deployment | Scalability | Security |
|---|---|---|---|---|
| Milvus | Open-source; infra cost only, managed options via Zilliz Cloud | Kubernetes, bare metal, cloud | High; distributed query and storage separation | RBAC patterns via platform controls, TLS and storage encryption depend on deployment |
| Pinecone | Fully managed usage-based pricing | SaaS only | High with minimal ops burden | Strong managed isolation and encryption, less infrastructure control |
| Weaviate | Open-source and managed cloud | Kubernetes, Docker, managed cloud | Good horizontal scale for mixed vector and object workloads | TLS, API auth, and cloud controls available |
Troubleshooting
Error 1: etcd unavailable
Log sample:
[2024/05/12 09:14:22.184 +00:00] [ERROR] [etcd_util.go:52] ["failed to connect to etcd"] [endpoint="milvus-etcd:2379"] [error="context deadline exceeded"]
Fix: verify etcd pod health, DNS resolution, and that port 2379 is allowed by NetworkPolicy.
Error 2: object storage write failure
Log sample:
[2024/05/12 09:17:41.002 +00:00] [ERROR] [minio_kv.go:89] ["failed to save binlog"] [bucket=milvus-prod-data] [error="AccessDenied: Access Denied"]
Fix: validate bucket policy, access key, region endpoint, and server-side encryption permissions.
Error 3: collection not loaded
Log sample:
MilvusException: <MilvusException: (code=101, message=collection not loaded[collection=docs])>
Fix: call collection.load() after index creation and confirm query nodes have enough memory to load segments.
Best Practices
Do
- Use HNSW for low-latency interactive search where memory is sufficient.
- Use scalar filters such as
tenant_idordocument_typeto reduce candidate sets. - Separate environments into distinct namespaces and object storage buckets.
- Benchmark recall and latency with production embeddings before choosing index type.
Don’t
- Do not expose Proxy publicly without ingress authentication and IP restrictions.
- Do not mix tenants casually in one collection without strict filtering and access control.
- Do not autoscale blindly; query nodes need warm data and memory-aware tuning.
- Do not skip backup validation; test restore from object storage and metadata snapshots regularly.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI