Milvus Migrations
Milvus Upgrade
Section titled “Milvus Upgrade”OpenRAG has been upgraded from Milvus 2.5.4 to 2.6.11 to leverage the enhancements introduced in the latest releases, particularly the new temporal querying capabilities added in version 2.6.6+.
What’s New in 2.6.x
Section titled “What’s New in 2.6.x”Milvus 2.6.6+ introduced the TIMESTAMPTZ field type, which enables:
- Comparison and range filtering using standard operators (
=,!=,<,>, etc.) - Interval arithmetic — add or subtract durations (days, hours, minutes) directly in filter expressions
- Time-based indexing for faster temporal queries
- Combined filtering — pair timestamp conditions with vector similarity search
Example — basic comparison:
expr = "tsz != ISO '2025-01-03T00:00:00+08:00'"results = client.query( collection_name, filter=expr, output_fields=["id", "tsz"], limit=10)Example — interval arithmetic:
expr = "tsz + INTERVAL 'P1D' > ISO '2025-01-03T00:00:00+08:00'"results = client.query( collection_name, filter=expr, output_fields=["id", "tsz"], limit=10)
INTERVALvalues follow ISO 8601 duration syntax:
P1D= 1 dayPT3H= 3 hoursP2DT6H= 2 days and 6 hours.
Milvus Version Upgrade Steps
Section titled “Milvus Version Upgrade Steps”For the full official reference, see the Milvus upgrade guide.
Step 1 — Upgrade Milvus to 2.5.16 (intermediate step)
Section titled “Step 1 — Upgrade Milvus to 2.5.16 (intermediate step)”Milvus requires an intermediate upgrade to v2.5.16 before jumping to 2.6.x. This step must be done manually before updating OpenRAG.
Temporarily edit vdb/milvus.yaml to set the intermediate Milvus image:
milvus: image: milvusdb/milvus:v2.5.4 image: milvusdb/milvus:v2.5.16Then restart Milvus and wait for it to be healthy:
docker compose downdocker compose up milvus -dVerify it is running and healthy before continuing:
docker inspect milvus-standalone --format '{{ .Config.Image }}'# Expected: milvusdb/milvus:v2.5.16Step 2 — Update OpenRAG
Section titled “Step 2 — Update OpenRAG”Once Milvus 2.5.16 is healthy, stop all services and update OpenRAG to the new version. The updated vdb/milvus.yaml already includes Milvus 2.6.11 and the required MinIO and etcd upgrades.
docker compose downVerify that all containers are stopped:
docker ps | grep milvusPull or checkout the new OpenRAG release, then start the stack:
docker compose up -dConfirm the running Milvus version:
docker inspect milvus-standalone --format '{{ .Config.Image }}'# Expected: milvusdb/milvus:v2.6.11Schema Migrations
Section titled “Schema Migrations”OpenRAG ships a generic migration runner that discovers and applies all pending Milvus schema migrations in order. You never need to invoke individual migration scripts by hand.
Migrations are versioned. The runner reads the current schema version stored in the collection’s properties and only applies scripts that bring the collection forward (or backward) from that version.
Step 1 — Start only the Milvus container
Section titled “Step 1 — Start only the Milvus container”docker compose up -d milvusWait until Milvus is healthy:
docker compose ps milvusStep 2 — Dry-run (inspect, no changes)
Section titled “Step 2 — Dry-run (inspect, no changes)”docker compose run --no-deps --rm --build --entrypoint "" openrag \ uv run python scripts/migrations/milvus/migrate.py --dry-rundocker compose --profile cpu run --no-deps --rm --build --entrypoint "" openrag-cpu \ uv run python scripts/migrations/milvus/migrate.py --dry-runReview the output to confirm which migrations are pending and what changes they would apply.
Step 3 — Apply all pending migrations
Section titled “Step 3 — Apply all pending migrations”docker compose run --no-deps --rm --entrypoint "" openrag \ uv run python scripts/migrations/milvus/migrate.pydocker compose --profile cpu run --no-deps --rm --entrypoint "" openrag-cpu \ uv run python scripts/migrations/milvus/migrate.pyThe runner will apply each pending migration in order. For the add_temporal_fields migration (v0 → v1) this means:
- Adding the nullable
TIMESTAMPTZfieldcreated_at - Creating an
STL_SORTindex on that field - Stamping the collection with
schema_version=1so OpenRAG no longer reports a migration error on startup
Step 4 — Restart OpenRAG
Section titled “Step 4 — Restart OpenRAG”docker compose up --build -ddocker compose --profile cpu up --build -dTargeting a specific version
Section titled “Targeting a specific version”To upgrade or downgrade to a specific schema version rather than the latest:
# Upgrade to version 2 onlydocker compose run --no-deps --rm --entrypoint "" openrag \ uv run python scripts/migrations/milvus/migrate.py --target 2
# Downgrade to version 0 (resets version stamp and drops indexes)docker compose run --no-deps --rm --entrypoint "" openrag \ uv run python scripts/migrations/milvus/migrate.py --downgrade --target 0# Upgrade to version 2 onlydocker compose --profile cpu run --no-deps --rm --entrypoint "" openrag-cpu \ uv run python scripts/migrations/milvus/migrate.py --target 2
# Downgrade to version 0 (resets version stamp and drops indexes)docker compose --profile cpu run --no-deps --rm --entrypoint "" openrag-cpu \ uv run python scripts/migrations/milvus/migrate.py --downgrade --target 0Rollback
Section titled “Rollback”Milvus does not support dropping fields. A downgrade only removes indexes and resets the version stamp — fields remain in the schema but are unused by the application:
docker compose run --no-deps --rm --entrypoint "" openrag \ uv run python scripts/migrations/milvus/migrate.py --downgradedocker compose --profile cpu run --no-deps --rm --entrypoint "" openrag-cpu \ uv run python scripts/migrations/milvus/migrate.py --downgradeTo fully remove the fields you would need to recreate the collection from scratch.
Adding a New Migration Script
Section titled “Adding a New Migration Script”Migration scripts live in openrag/scripts/migrations/milvus/. The runner discovers them automatically — no registration step required.
Naming convention
Section titled “Naming convention”Files must follow the pattern N.short_description.py, where N is the target schema version as a positive integer:
openrag/scripts/migrations/milvus/ 1.add_temporal_fields.py ← brings the schema to version 1 2.your_new_migration.py ← brings the schema to version 2 migrate.py ← generic runner (do not rename)The numeric prefix determines execution order. Never reuse or change an existing version number.
Required module interface
Section titled “Required module interface”Each migration script must expose the following at module level:
| Name | Type | Description |
|---|---|---|
TARGET_VERSION | int | The schema version this script brings the collection to |
upgrade(client, collection_name, dry_run) | function | Applies the migration |
downgrade(client, collection_name, dry_run) | function | Reverts the migration (indexes only — fields cannot be dropped) |
Minimal template
Section titled “Minimal template”"""Milvus migration: <description> (schema version N-1 → N)"""
from pymilvus import DataType, MilvusClientfrom utils.logger import get_logger
TARGET_VERSION = N # replace with the actual version number
FIELDS_2_ADD = [ {"field_name": "my_field", "data_type": DataType.VARCHAR, "max_length": 256, "nullable": True},]
INDEXES_2_ADD = [ # add index specs here if needed]
logger = get_logger()
def upgrade(client: MilvusClient, collection_name: str, dry_run: bool = False) -> None: # Add fields and indexes, then bump the version property. ...
def downgrade(client: MilvusClient, collection_name: str, dry_run: bool = False) -> None: # Drop indexes and reset the version property. # Note: Milvus does not support dropping fields. ...Use 1.add_temporal_fields.py as a reference implementation for the full upgrade/downgrade pattern.