AI-Chain

Do Not Let RAG Stall at Retrieval: How Milvus Turns Vector Search into Scalable AI Infrastructure

Share:
Do Not Let RAG Stall at Retrieval: How Milvus Turns Vector Search into Scalable AI Infrastructure

Do Not Let RAG Stall at Retrieval: How Milvus Turns Vector Search into Scalable AI Infrastructure

When I review RAG projects, I often see the same assumption: once documents are chunked, embeddings are generated, and the results are passed to an LLM, the system is finished. That can be true for a quick prototype. But as data grows, queries increase, tenants need isolation, and documents must be updated continuously, the first bottleneck is often not the generative model. It is the retrieval layer.

That is why I think Milvus deserves a focused look. It is not another chatbot and it is not a framework for wrapping prompts. It is an open source database designed for vector data, similarity search, and retrieval at scale. The official documentation presents a path from Milvus Lite on a laptop, through a single machine deployment with Docker, to a distributed Kubernetes deployment. That positioning is practical: validate a RAG idea locally first, then change the deployment shape when traffic, data volume, or reliability requirements justify it, instead of committing to heavy infrastructure on day one.

As of the GitHub API snapshot taken on August 31, 2026, the milvus-io/milvus repository showed 45,901 stars and 4,212 forks, with push activity on the same day. Star count does not prove that a tool is right for your team, but it does show that Milvus is more than a conceptual demo. In this article I will not only list its features. I will examine the practical questions an AI team should ask: what problem does it solve, how do you start, when should you scale up, and which limitations should not be hidden by marketing language.

The short version: Milvus makes the retrieval layer an evolvable system

My view is that Milvus belongs on the shortlist when an AI application has moved beyond a one-off demo and needs persistent embeddings, metadata filters, multiple search strategies, or a credible path from thousands of records to much larger collections.

The important design choice is not the abstract claim that search is fast. It is the separation of responsibilities that are often tied together: data storage, vector indexes, query nodes, data ingestion, and scaling strategy. The official architecture documentation explains that distributed deployments can separate compute from storage and scale query nodes and data nodes according to read and write workloads. A RAG service with heavy reads does not have to scale in exactly the same way as a pipeline continuously writing new documents.

On the developer experience side, Milvus offers three deployment modes:

1. Milvus Lite: a Python library embedded in an application, with data stored in a local file. It is suitable for notebooks, proof of concept work, and edge devices.

2. Milvus Standalone: a single machine server run with Docker. It is a better fit when several users or services need a server endpoint, or when the database lifecycle should be separated from one Python process.

3. Milvus Distributed: a Kubernetes deployment that applies Milvus cloud native and horizontal scaling capabilities to larger environments.

This progression is more realistic than telling every team to begin with Kubernetes. A prototype should validate data and retrieval quality. A production system must also address availability, isolation, observability, and cost.

Why does RAG need a dedicated vector database?

Text, images, and audio are not naturally easy to compare in a conventional database column. An embedding model converts these unstructured inputs into high dimensional vectors, and semantically related items tend to be close together in vector space. The retrieval step in RAG converts a user question into a query vector, finds nearby items, and sends the selected content to a generative model.

That apparently simple flow contains at least four kinds of data:

  • The vectors themselves, such as float, binary, or sparse vectors.
  • The original content, such as document passages, image descriptions, or text chunks.
  • Metadata, such as source, tenant, permissions, timestamps, and document type.
  • Index and search settings, such as HNSW, IVF, DiskANN, distance metrics, and top K.

A generic storage layer may be sufficient at the beginning. But when a query must combine semantic similarity with conditions such as tenant, product version, permission scope, or document type, retrieval becomes a data modeling and query planning problem. Milvus stores vector fields and scalar fields in the same collection, allowing vector search to use filter expressions. For RAG applications, that is one of its most practical benefits.

Start with the official quickstart: validate the data path with Milvus Lite

I recommend starting with the official Milvus Lite quickstart instead of immediately building a distributed cluster. The documented prerequisite is Python 3.8 or newer. Then install pymilvus:

pip install -U pymilvus

Creating a local database requires only a file path:

from pymilvus import MilvusClient
client = MilvusClient("milvus_demo.db")

Next create a collection. The official example uses 768 dimensional vectors and relies on basic defaults for the first experiment:

if client.has_collection(collection_name="demo_collection"):
    client.drop_collection(collection_name="demo_collection")
client.create_collection(
    collection_name="demo_collection",
    dimension=768,
)

There is an important beginner trap here: vector dimension is not a cosmetic setting. It is part of the collection schema contract. If your embedding model outputs 1536 dimensions, the collection cannot remain at 768. When you replace the model, you also need to decide whether existing data must be re-embedded.

The official quickstart then uses the pymilvus[model] default embedding function to turn three sentences into vectors and inserts each entity as a dictionary:

from pymilvus import model
embedding_fn = model.DefaultEmbeddingFunction()
docs = [
    "Artificial intelligence was founded as an academic discipline in 1956.",
    "Alan Turing was the first person to conduct substantial research in AI.",
    "Born in Maida Vale, London, Turing was raised in southern England.",
]
vectors = embedding_fn.encode_documents(docs)
data = [
    {"id": i, "vector": vectors[i], "text": docs[i], "subject": "history"}
    for i in range(len(vectors))
]
client.insert(collection_name="demo_collection", data=data)

Then encode the query sentence and perform a vector search:

query_vectors = embedding_fn.encode_queries(["Who is Alan Turing?"])
res = client.search(
    collection_name="demo_collection",
    data=query_vectors,
    limit=2,
    output_fields=["text", "subject"],
)

This example is worth keeping because it closes the smallest understandable loop: text, embeddings, collection, insert, and search. Do not begin by chasing a complicated reranker or agent workflow. First prove that a known input can return a reasonable retrieval result. Only then discuss scale.

The key is not only ANN: vectors and conditions must be searchable together

Pure vector search returns items close to a query vector, but real applications usually have conditions. A support agent may search only the product line it owns. A legal assistant may retrieve only documents within the user permission scope. An answer may be allowed to cite only documents from a particular version. Milvus treats these non-vector fields as scalar data and supports a filter during search:

res = client.search(
    collection_name="demo_collection",
    data=embedding_fn.encode_queries(["tell me AI related information"]),
    filter="subject == 'biology'",
    limit=2,
    output_fields=["text", "subject"],
)

This is not a minor feature. It separates a RAG demo from a product-ready retrieval layer. Without metadata filtering, it is easy to send the wrong tenant, an outdated version, or restricted material into the LLM context. With filters, retrieval can align with permissions, versions, and business conditions. However, a filter is not a complete authorization system. The application still has to authenticate users and determine their scope, while the database should be configured with capabilities such as authentication, TLS, and RBAC.

The Milvus documentation also lists more than one search mode: ANN search for approximate nearest neighbors, range search for vectors within a distance range, hybrid search across multiple vector fields, full text search based on BM25, and reranking to adjust initial results. These capabilities reflect a practical reality: good RAG is rarely determined by dense embeddings alone. Semantic similarity, keyword matches, metadata filters, and reranking often need to work together.

From vector search to RAG: where does Milvus fit?

RAG is not a single component. It is a pipeline. A common path looks like this:

1. Read documents and split them into suitable chunks.

2. Use an embedding model to convert chunks into vectors.

3. Store vectors, original text, and metadata in Milvus.

4. Convert a user question into a query embedding.

5. Retrieve top K documents with Milvus, adding filters or hybrid search when needed.

6. Assemble the retrieved passages into context and send it to an LLM.

7. Preserve sources, distances, and version information so the answer can be traced.

The official Build RAG with Milvus tutorial follows this path. It uses pymilvus, openai, requests, and tqdm, creates embeddings, builds a collection through MilvusClient, inserts data, searches for relevant passages, and assembles the retrieved context for an LLM. The tutorial also makes the URI boundary clear: a local file uses Milvus Lite, while a larger deployment can use a Docker or Kubernetes Milvus server such as http://localhost:19530.

I strongly recommend measuring retrieval quality separately from generation quality. A poor LLM answer may mean that the model misunderstood the context, or it may mean that Milvus retrieved the wrong documents. At minimum, log the query, retrieved chunks, distance, filters, embedding model, and final answer. Otherwise you can see a bad result without knowing which layer caused it.

When should you move from Lite to Docker?

Milvus Lite is fast to start, but it is not the final destination for every workload. I would consider Standalone when one or more of the following conditions appears:

  • Multiple services need to share one vector database over a network.
  • Data ingestion and queries should be managed independently rather than inside one Python process.
  • Persistence, monitoring, backup, and deployment lifecycle should be separated from the application.
  • The deployment needs a more production-like path to authentication, TLS, or RBAC configuration.

The official Docker Compose guide asks you to install Docker, download the compose configuration supplied by the Milvus repository, and start the services:

wget https://github.com/milvus-io/milvus/releases/download/v2.6.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
sudo docker compose up -d
docker compose ps

The version in an example should be replaced with the version named by the current official documentation or release. Do not pin a sample version forever. The official guide says that Standalone starts milvus-etcd, milvus-minio, and milvus-standalone by default. The main service normally exposes port 19530 locally, and a WebUI is available on port 9091 for observing the instance. After startup, run docker compose ps and confirm that the containers are healthy before connecting an application. Seeing a container process is not the same as having a ready service.

When data volume, query traffic, or availability requirements grow further, evaluate a Kubernetes Distributed deployment. This is not merely a matter of replacing one YAML file with Helm. It means operating object storage, metadata, message queues, node configuration, monitoring, backup, upgrades, and failure recovery together. A team without the operational capacity should not choose distributed deployment just because the word sounds more professional.

Indexes and performance: do not mistake a benchmark for your result

Milvus supports vector indexes such as HNSW, IVF, FLAT, SCANN, and DiskANN, as well as quantization variants and GPU acceleration options. Every choice represents tradeoffs among query latency, recall, memory, index build time, and update cost.

Lower latency does not mean that the candidate set can be reduced without limits. If index parameters lower recall, RAG may miss the key passage and produce a fluent but incorrect answer. On the other hand, maximizing recall can send too much noise into the context, increasing token cost and confusing the model.

For that reason, I would not turn performance claims in official documentation into a guarantee for your system. Test with your own embedding model, data distribution, filters, top K, hardware, and concurrency. Measure recall, p95 latency, ingestion throughput, index rebuild time, and cost. Milvus provides a tunable retrieval engine; it does not remove the need for experiments.

Limitations and common traps

1. Milvus does not choose the embedding model for you

A vector database can search vectors and distances, but it cannot make an unsuitable embedding model suitable. If the model does not fit your language, domain, or document type, a faster database will only find irrelevant content faster. Model choice, chunk size, overlap, query rewriting, and reranking should be evaluated together.

2. Dimension and distance metrics must remain compatible

The collection dimension, inserted vectors, and query vectors must be compatible. Changing a model is not a one-line edit. If the new model has a different dimension or semantic distribution, you will usually need a new collection and a full re-embedding process.

3. Metadata filters are not a complete permission system

A tenant_id or user_id filter can reduce accidental cross-tenant retrieval, but it does not replace identity verification, authorization logic, or security auditing. The official documentation lists authentication, TLS, and RBAC as security capabilities. A real deployment still needs to check default settings, network exposure, secret management, and backup permissions.

4. Distributed deployment creates operational work

Kubernetes can provide horizontal scaling and recovery, but it also requires storage, metadata, message queue, version upgrades, and monitoring to be managed. If your dataset contains only a few tens of thousands of records and traffic is low, Lite or Standalone may be the more economical choice.

5. A documentation example is not a production configuration

A quickstart is designed to close the loop in minutes, so it uses a default schema, simple data, and a demonstration model. Before production, add schema versioning, deletion policy, re-embedding workflows, index lifecycle management, observability, and disaster recovery exercises.

How would I organize a practical adoption plan?

I would divide adoption into four stages instead of moving the entire platform at once.

Stage one, validate retrieval usefulness. Use Milvus Lite with a small set of real documents. Fix the embedding model and chunking rules, then create a query set with expected answers. First measure whether the correct source appears in top K. Do not add a complicated agent yet.

Stage two, validate metadata and security boundaries. Add tenant, product version, language, and permission fields. Test that filters exclude material that must not appear. Treat failure cases as first class tests rather than showing only successful examples.

Stage three, validate hybrid search and cost. Test dense search, full text search, and hybrid search separately on proper nouns, product codes, and long natural language questions. Observe which strategy improves recall, then estimate embedding, storage, query, and reranking costs.

Stage four, choose the deployment shape. Move to Standalone when the application needs a shared service. Plan Distributed only when query and ingestion have different scaling needs, or when availability and data volume genuinely require it. Every upgrade should have a measurable reason.

Who should try Milvus first?

I think Milvus is a good first experiment for teams that:

  • Are building an enterprise knowledge base, support assistant, document search system, or recommendation service.
  • Already have an embedding and LLM flow, but retrieval data is scattered across files, caches, or an unsuitable storage layer.
  • Need metadata filtering, hybrid search, or multiple vector fields.
  • Want to move from a local prototype to a self-hosted service while keeping options among Python, Go, Java, and Node.js SDKs.
  • Can operate at least Docker and basic monitoring, or are willing to use a managed service to reduce operations work.

On the other hand, Milvus may be too heavy if an application only needs a one-time similarity comparison over a dozen records, or if the team does not want to operate any database. Lite lowers the barrier, but embeddability does not mean every small use case needs a full vector database.

Conclusion: the next RAG bottleneck is usually data and retrieval engineering

My conclusion is not that Milvus is automatically the best vector database. It is that Milvus turns the most underestimated layer of RAG into an engineering component that can evolve. You can start with a local file, establish a measurable retrieval baseline, move to Docker when service boundaries appear, and use Kubernetes only when distributed architecture is justified.

The value of this path is that it directs the team toward the right questions: which data should be retrieved, whether results respect permissions and versions, how retrieval quality is measured, and whether the system remains observable and maintainable as it grows. An LLM that generates text cannot repair incorrect retrieval. If the context is wrong at the start, a fluent answer only makes the error look more convincing.

If your next RAG project is already facing data growth, tenant isolation, hybrid search, or deployment evolution, I would start by running a measurable retrieval baseline with Milvus Lite and then decide whether to scale up. Do not begin with the question "Should we use Milvus?" Begin with the question "What responsibilities must our retrieval layer carry next?"


References