Milvus 3.0 Went Lake-Native: What Actually Changed
Here's the thing about upgrading to Milvus 3.0: you can do it, restart everything, watch your queries run exactly as before, and conclude that the release was overhyped. That's a reasonable conclusion and it's wrong. The headline feature is switched off by default, and so are the new indexes.
So before you upgrade, it's worth knowing what actually changed, what you have to opt into, and which of those opt-ins you can't undo.
Lake-native means your data stops moving
The old deal with a vector database was simple and annoying. Your data lives in object storage as Parquet. To search it, you copy it into the vector database. Now you own two copies, a sync job, and an argument about which one is correct.
External Collection breaks that. Milvus references lake files in place, builds indexes over them, and searches them without ingesting anything. Parquet, Lance, Iceberg and Vortex are all supported, read-only, kept current with incremental refresh.
3.0.0 pushes it further than the beta did. External fields can now feed function output fields, so BM25 sparse vectors, MinHash signatures and text embeddings get built inside Milvus without copying the source table. Refresh handles additive schema evolution too: when the external table gains a column, Milvus patches the affected segments instead of rebuilding the whole collection.
Underneath sits Storage V3, codenamed Loon, which is manifest-based columnar storage on object storage. Remember that name, because it's the thing that's off by default.
The feature that will actually save you a weekend
Schemas don't hold still. Embedding models get replaced, features iterate, fields get deprecated. Historically each of those meant a full-collection rebuild with downtime, or a double-write scheme somebody had to babysit.
3.0.0 lets you add, backfill and drop columns while the collection keeps serving.
Backfill works in both directions, and the external path is the interesting one. Add a column, snapshot the collection as a consistent starting point, run your job offline, write the values back, and Milvus indexes the new column incrementally. That turns an embedding-model upgrade across hundreds of millions of rows into a hot-path operation instead of a migration weekend.
Inner backfill covers the values Milvus can compute itself. Attach a BM25 or MinHash function to an existing collection and the output field gets computed over existing data automatically.
Sparse search got a real overhaul
If you run hybrid or full-text search, this is the part that matters.
The sparse index was rebuilt around SINDI, with Block-Max WAND and Block-Max MaxScore alongside it, plus inverted-list compression and configurable quantization. Zilliz's internal benchmarks put the compressed BM25 index at roughly 3x smaller than the 2.6 sparse index at comparable recall, and SINDI at up to about 10x the QPS of MaxScore on learned sparse embeddings.
Those are vendor numbers on vendor workloads, so treat them as direction rather than a promise. The architectural change is real either way.
TEXT fields are the other retrieval change worth knowing. Long text is now first-class with no storage-side length limit. Values under 64 KB stay inline, larger ones go to partition-level LOB files in Vortex format with the column holding only a file reference. LOB files are shared across segments, so compaction moves references rather than rewriting text. For RAG that means you fetch vectors and source text from the same store in one IO, and you stop operating a separate blob store next to it.
What you have to turn on, and what you can't turn off
This is the section I'd read twice.
| Change | Default | How to enable |
|---|---|---|
| Storage V3 (Loon), needed for Snapshot and TEXT | Disabled | common.storage.useLoonFFI |
| New vector index algorithms | Opt-in | dataCoord.targetVecIndexVersion=10 |
| New scalar index algorithms | Opt-in | dataCoord.targetScalarIndexVersion=4 |
Now the part that bites. Milvus guarantees 2.6 to 3.0 compatibility and rollback, so you can go back. But once you enable a feature that changes the serialized data format, rollback is gone. Storage V3 is exactly such a feature. So the safe sequence is: upgrade, run on 3.0 for a while with the old format, confirm your workload is healthy, and only then flip Loon on. If you enable everything on day one you have thrown away your escape hatch to save a week.
One more: GPU images moved to CUDA 12.9 and no longer preserve Ubuntu 20.04 GPU compatibility. Check your base image before you schedule the upgrade, not after.
Also in the box
Woodpecker, the write-ahead log at the core of the streaming write path, can now run as its own service rather than embedded in other nodes. That means independent scaling, fault isolation and its own observability, which matters mostly for large clusters and write-heavy workloads.
Faceted search landed on the search path, so you specify a facet field at search time and get top facet values back with COUNT and AVG annotations, instead of over-fetching and counting client-side. There's also a Function Chain API for composable reranking, and a FAISS passthrough index that accepts arbitrary index-factory strings so existing Faiss recipes reproduce directly.
Should you upgrade?
If you're on 2.6 and content, there's no fire. Nothing is being taken away from you this quarter.
Upgrade when one of these is true: you're paying to keep a second copy of lake data inside Milvus, you have an embedding-model migration coming and no appetite for downtime, or your sparse and full-text indexes are eating more RAM and disk than you'd like.
Just do it in stages. Upgrade first, enable Loon second, raise index versions third, and leave time between each.
You can run managed Milvus on Elestio if you'd rather not operate the coordinator, query nodes and object storage yourself. Vector workloads are memory-hungry, so size honestly rather than starting at the entry config: VMs begin at $16/month for 2 vCPU and 4 GB RAM, but anything at real scale wants considerably more.
Thanks for reading ❤️ See you in the next one 👋