Migrating to Milvus

Tooling, deployment choices and lessons from real migrations

Sep 2 · 2026

Simon Hearne
Solutions Architect · Zilliz

Last time: closing the search gap

Milvus 3.0 brought full-text retrieval, sorting, aggregation and faceting into one engine, and we put it head to head with Elasticsearch on live queries.


lab Run the comparison yourself

Docker Compose, one dataset, side-by-side queries against Elasticsearch and Milvus.

github.com/simonhearne/milvus_es_lab

recording Watch the previous webinar

The full walkthrough of what Milvus 3.0 added and how the two engines compared.

youtu.be/XxcA1NSQnCo

Today

From comparison to migration: tooling, deployment choices and lessons from real migrations.

What we'll cover today

The migration path

The major stages and decisions in an Elasticsearch / OpenSearch migration to Milvus or Zilliz Cloud

Tooling + real walkthrough

VTS (open source) and Zilliz Migration Service (managed), then a real migration workflow end to end

Licensing & commercial tradeoffs

Licensing models and the commercial considerations behind Elastic Cloud (ESS), OpenSearch and Zilliz Cloud

Deployment choices

Open-source Milvus, Zilliz Cloud and Zilliz BYOC across operating model, control and security

Customer migration stories

Why real teams moved, how they approached it, and what they learned along the way

Live AMA

Around 20 minutes for your migration, architecture and evaluation questions

Why migrate?


Vector search is a plugin

  • One HNSW graph per segment: query cost grows with every merge
  • Hybrid search is stitched in your code: Rexera gained 40% accuracy in one engine
  • Hard ceilings: Orfium maxed out near 500K references, now holds 250M+ vectors

Seen at Orfium, Rexera, OpusSearch

Read & write fight for resource

  • Indexing takes nodes down: 123RF's daily ingest dropped OpenSearch nodes
  • Latency swings under load: Jobright's P95 was 200 to 500ms, now under 50ms
  • Writes wait on graph rebuilds: HelloBike saw ~100x faster writes after migrating

Seen at 123RF, Jobright, HelloBike

Cost scales with data, not value

  • RAM-constrained: UNIwise went from hundreds of GB to under 32GB
  • Compute and storage grow together: sized for peak, paid for around the clock
  • The bill: halving cost post-migration is common

Seen at 123RF, Rexera, UNIwise

None of these are tuning problems. They are architecture problems.

The migration path

Every Elasticsearch / OpenSearch migration passes through the same stages:

Assessworkload & indices Mapschema & analyzers Movedata Validateparity & recall Cut overbackfill / dual-write Assessworkload & indices Mapschema & analyzers Movedata Validateparity & recall Cut overbackfill / dual-write what we cover today
  • Assess: which indices, which query patterns, what actually needs to move
  • Map: mappings to collections, analyzers to Milvus analyzers, scores to scores
  • Move: bulk transfer plus change capture, without re-embedding
  • Validate: side-by-side results before anything is switched
  • Cut over: staged, reversible, boring by design

Migration tooling

open source

VTS

Open-source migration tool: streams data from Elasticsearch / OpenSearch into Milvus. Run it yourself, inspect every step

VTS tool screenshot
zilliztech/vts

managed

Zilliz Migration Service

Managed service built on VTS: source into Milvus or Zilliz Cloud with the operational heavy lifting handled for you

Zilliz migration service screenshot documentation

The lab: three migrations, one dataset

Same ~100k Amazon product documents, loaded from one Parquet: 2×1024-d COSINE FP32 vectors, no re-embedding, same BM25 analyzer chain.

Elasticsearch 9.5.2Milvus 3.0

VTSlocal

Docker to Docker on the laptop. Open-source VTS, every step inspectable

Elastic Cloud 9.5.2Zilliz Cloud

VTScloud

Managed to managed, both in AWS eu-west-1. The same VTS job pointed at two cloud endpoints

OpenSearch 3.7.0Zilliz Cloud

ZMSmanaged

AWS OpenSearch Service into a second Zilliz Cloud cluster via Zilliz Migration Service, from the console

Where everything runs

local · docker

Elasticsearch 9.5.2

Single node, 8 GB / 4 CPU, 4 GB JVM heap. 1 primary / 0 replica

local · docker

Milvus 3.0

Standalone with etcd + MinIO, 8 GB / 4 CPU, 1 replica

aws eu-west-1

Elastic Cloud 9.5.2

2 × c6gd data nodes, 4 GB each. 1 primary / 1 replica

aws eu-west-1

AWS OpenSearch 3.7.0

2 × c8g.large.search data nodes, 4GB each with 120 GB gp3. 5 primary / 1 replica

aws eu-west-1

Zilliz CloudVTS target

1 CU, performance-optimized

aws eu-west-1

Zilliz CloudZMS target

1 CU, performance-optimized. A second, separate cluster

Every cloud service in AWS eu-west-1 to control for network latency. The client for every figure is one laptop: Apple M5, Docker Desktop with all 10 cores, on home fibre internet in London (1 Gbps down / 100 Mbps up).

Cloud lab costs per day

Elastic Cloud $14.74 / day

Cloud Standard: $9.93 for two c6gd data nodes, $2.48 Kibana node, $1.24 mandatory master, ~$1 data transfer, $0.09 storage. No auto scale-down, no suspend option.

Source: cloud.elastic.co/pricing
AWS OpenSearch Service $7.88 / day

$6.82 for two c8g.large.search nodes, $1.06 for 120 GB gp3 each, no dedicated master. No auto scale-down, no suspend option.


Source: calculator.aws
Zilliz Cloud $4.69 / day

Dedicated Standard: $4.68 compute, $0.01 storage including backups. Auto-scales storage and throughput both ways, can suspend to zero compute when not in use.

Source: zilliz.com/pricing/pricing-guide

All AWS eu-west-1, equivalent hardware specifications, lowest commercial tier. List price per 24h of runtime.

How I run the migration

Schema first. The target collection exists before VTS is asked to move a row, so every field and index is explicit up-front.

1 · before the job

Decide the schema

Build the empty collection first: fields, analyzer, BM25 function and indexes, then load it. Indexes are enabled on vector fields and common filter fields.

2 · one JSON document

Submit the job

Source Elasticsearch scrolls the index 500 docs at a time, sink Milvus inserts in batches of 500. Posted to the VTS REST API, polled until SinkWriteCount reaches 97,894, then flush. The cloud leg is the same job with two endpoints swapped

3 · by name, then by target type

How VTS maps fields

Every projected source column needs a Milvus field of the same name; the sink reads the collection schema first and converts each value to the declared type. Two explicit mappings: array_column (ES has no array type) and pk = parent_asin. Computed fields like text_sparse are not sent

ZMS simplifies: the wizard reads the existing index and creates the target collection. Its analyzer step takes our filter chain and the text_sparse name, so BM25 is still declared before rows land. Indexes come afterwards (you can always add indexes to existing fields).

Mapping the schema

Every field type an index needs has a home in Milvus. Most of the mapping is mechanical: the analyzer chain and the vector index are the two decisions that actually matter.

Need Elasticsearch / OpenSearch Milvus Notes
Primary key _id, implicit string VARCHAR or INT64 with is_primary, auto_id optional Keep the source _id: it is the join key for validation
Numbers, booleans long integer double float boolean INT64 INT32 INT16 INT8 DOUBLE FLOAT BOOL nullable and default_value per field
Exact-match strings keyword VARCHAR + INVERTED or BITMAP index Filters, sort, group by
Full-text, BM25 text + analyzer VARCHAR enable_analyzer → BM25 FunctionSPARSE_FLOAT_VECTOR Same tokenizer and filter chain, declared per field
Term, phrase match term match_phrase enable_matchTEXT_MATCH, PHRASE_MATCH in the filter A boolean pre-filter, not a scorer
Dense vectors dense_vector / knn_vector; HNSW with bbq, int8, faiss compression_level FLOAT_VECTOR (FLOAT16 BFLOAT16 INT8 BINARY); HNSW IVF_RABITQ DISKANN AUTOINDEX Several vector fields per collection, hybrid search across them
Learned sparse sparse_vector / rank_features (ELSER, neural-sparse) SPARSE_FLOAT_VECTOR + SPARSE_INVERTED_INDEX, metric IP The same field type BM25 writes into
Arrays any field, multi-valued ARRAY<type> with max_capacity array_contains, array_length in filters
Objects, nested object nested flattened JSON + JSON path index; dynamic fields for unmapped keys Nested docs flatten, or become a second collection
Dates date TIMESTAMPTZ, or INT64 epoch Range filter and sort either way
Geo geo_point geo_shape GEOMETRY, WKT in, ST_* predicates in filters Supports distance, bounding polygons etc.

While that runs: the six-phase strategy

updates index embed query search New search Milvus / Zilliz Cloud 2b · reindex from the source (re-embed) 2a · migrateVTS / ZMS 3 · dual ingest 4 · dual run compare 5 · cut over rollback 6 · retired System ofrecord Embeddingservice Queries Current searchElasticsearch / OpenSearch
  1. Steady state: updates flow source → embedding → search; queries embed, then search
  2. Backfill: stand up the new system, then reindex from the source (clean slate, re-embed) or migrate the index (keeps vectors, no re-embedding)
  3. Dual ingest: write to both stacks so the new index keeps pace with production
  4. Dual run: every query hits both engines; serve from the old one, compare results, recall and latency from the new
  5. Cut over: route live traffic to the new system; the old stack stays warm as rollback
  6. Decommission: retire the old cluster once confidence and the contract agree

While that runs: how the numbers are made

Ground truth

Exact, not sampled: brute-force top-100 over all 97,894 normalized vectors for 200 fixed queries drawn from the corpus. Text and image spaces scored separately, each against its own truth

Filter specificity

Filtered benches use 20 ≤ price ≤ 60: 27,300 rows, 27.9% of the corpus. Ground truth is recomputed over only those rows, and the bench filter is the same constant by construction

Latency & throughput

One discarded warm-up, then every query timed sequentially: no queueing noise in p50 / p95. QPS is a separate phase with a bounded pool of 8. All client-observed

Recall & BM25 parity

recall@10 is the overlap with the exact top-10. BM25 has no ground truth, so a fixed 20-query set is scored as overlap@10 against the ES-local baseline

Honesty rules

Benches never run concurrently across systems. Latency and QPS are client-observed from my laptop and are illustrative; recall, filtered recall and BM25 overlap are the architectural numbers. For hardware-scale claims, see published VDBBench studies.

Licensing and commercial tradeoffs

open source

OpenSearch

  • Apache 2.0: forked from Elasticsearch 7.10.2 in 2021, Linux Foundation since 2024
  • Security, alerting, ML and k-NN all ship in the one licence, no paid tier

source available

Elasticsearch

  • Left Apache 2.0 in 7.11 for SSPL + Elastic License 2.0; AGPLv3 added in 8.16 as a third option
  • Free tier covers vector search, bbq_hnsw and core security; RRF, bbq_disk, semantic_text and inference service require Enterprise license

open source

Milvus

  • Apache 2.0 since 2019; LF AI & Data graduated project, vendor-neutral governance
  • No paid tier: every feature is in the open build with one codebase for lite, standalone & distributed

aws managed

Amazon OpenSearch Service

  • Infrastructure pricing: instance-hour (EC2+66%) plus EBS storage
  • No feature tiers: every plugin is included in the instance price

elastic managed

Elastic Cloud (ESS)

  • Infrastructure pricing: GB-hour by hardware profile, plus data transfer and snapshot storage
  • Subscription tier (Standard, Gold, Platinum, Enterprise) chosen per deployment and gates features and SLA

zilliz managed

Zilliz Cloud

  • Infrastructure pricing: CU-hour by performance profile; plus data transfer and storage
  • Same Milvus API - open source and cloud stay interchangeable; BYOC and marketplace billing on AWS, GCP and Azure

Deployment choices

Self-managed software

Milvus

The most popular open source vectorDB

Operations
You deploy, upgrade, scale and monitor it
Control
Total: any infrastructure, any config, source access
Security
Your responsibility, end to end

Milvus Lite · Docker · Kubernetes

Bring your own cloud

Zilliz Cloud BYOC

Managed control plane, deployed in your VPC

Operations
Zilliz operates the data plane inside your account
Control
Your account, your network, your keys
Security
Data never leaves your cloud boundary

AWS · Google Cloud · Azure

Milvus

Migrate once: one Milvus API & SDK across all three with seamless migration

You're in good company

123RF: image search at 200M+

OpenSearchZilliz Cloudtext-to-image and reverse image searchSource: zilliz.com/customers/123rf

Query latency 30ms - 50ms

Average, down from ~100ms

Search infra cost Halved

vs. unstable OpenSearch environment

Library 200M+

Image vectors, growing daily

Bulk indexing Hours

10M+ per job, no query impact

why they moved

  • Latency and throughput unpredictable under real production traffic
  • Daily ingest of new assets dropped nodes, so DevOps was firefighting the cluster

what they built

  • CLIP embeddings, iterated across two model versions; any model, not a vendor's
  • Cluster scaled up and down on expected load; a custom Boost Ranker carries the business ranking rules

what they learned

  • Evaluated Pinecone and Weaviate too: some cost more than the problem they were solving
  • Image search first, then video and audio follow on the same platform

“Moving to Zilliz Cloud didn't just cut our infrastructure costs dramatically; it gave our engineering team the confidence that search will scale with our business instead of holding it back.” Su-Meng Yong, Engineering Team Lead, 123RF

Plaud: agent memory at 10B+

OpenSearchZilliz Cloudagentic RAG and memory for 2M+ devices in 170+ countriesSource: zilliz.com/customers/plaud

Scale 10B+

Embeddings per region, and growing

Latency <200ms

Average, <800ms at P99

Backfill Days

Billion-scale with no query impact

Downtime Zero

Incl. new fields & indexes and upgrades

why they moved

  • Performance and cost unacceptable at billion-scale semantic and hybrid workloads
  • Consumer memory is mostly cold: ignored for months, then needed instantly, so it has to be cheap to keep

what they built

  • Hybrid search with decay ranking and document-type weighting; Plaud keeps the fusion and ranking logic
  • Multi-region clusters with custom encryption keys, read/write isolation and hot/cold tiering

what they learned

  • Migrate once: choose infra for end-state scale
  • Benchmark scalar filtering and vector search together; that combination is the real test

“Zilliz Cloud gives us a solid foundation we can trust for agentic memory retrieval at a massive scale, so our team can put its energy into product innovation and user experience, not the plumbing beneath it.” Charles Liu, Co-founder & CTO, Plaud

Live AMA: bring your migration questions.

Live AMA: bring your migration questions.Thank you!

Thank you!

Questions?

simon @ zilliz.com

Simon Hearne
Solutions Architect · Zilliz