# Milvus hands-on test, 2026-10-24

Protocol: `docs/tool-evidence-grade-spec.md`, section 7.5 (Milvus on owned hardware). Result: **worked**. Every required key passed on the third attempt at the client step, and the first two attempts failed for reasons unrelated to Milvus's correctness (a bug in the test script, then the WSL2 virtual machine shutting down while idle). Both are described below, in order. Times are UTC; the machine runs on Pacific time (UTC-7), so 14:07Z is 07:07 local.

## Who, where, what

- Run by an AI agent session (Claude Code) on the site owner's Windows 11 desktop. The owner did not take any step during the run.
- Docker Engine 29.6.2 with Compose v5.3.1 runs inside the WSL2 `Ubuntu` distro (Ubuntu 24.04.4, kernel 6.18.33-2, WSL 2.7.10-0). There is no Docker Desktop. The WSL machine sees 28 CPUs and about 15 GiB of memory. There is no `.wslconfig`, so WSL defaults apply.
- The client is a Windows venv: Python 3.14.7, `pymilvus` 3.0.2, NumPy 2.5.4, grpcio 1.84.1. It reaches Milvus at 127.0.0.1 through WSL2's localhost forwarding.
- Milvus standalone v3.0.3 from the vendor's Docker Compose file for that release (pinned release URL, SHA-256 `f0de3ec2f4954bdcb1ec6dcb9862979c00a12632b2181c7529074f510d281573`), with `quay.io/coreos/etcd:v3.5.25` and `milvusdb/minio:RELEASE.2024-12-18T13-15-44Z`. Image IDs are in `session.log`.
- Another container, `windows-4070ti` (gpustack), already existed on this Docker host. It starts by itself whenever the engine starts, so it ran beside the test and shared CPU and memory. It was not touched.
- Cost: none. Owned hardware, no credits.
- The vendor docs were read through a fetch tool that summarises pages, not in a browser. Quoted commands and sentences are as that tool returned them. The pages read are listed in the record's `docsFollowed`.

## The fixed dataset

`dataset.json` holds 1,000 synthetic vectors of 128 dimensions (standard normal, seed 1024, rounded to four decimals), and `queries.json` holds 20 query vectors with their top 10 ids from a brute-force NumPy computation (exact squared L2 in float64, ties to the lower id). Both were written by `scripts/tool-verifications/milvus/milvus_check.py generate`, and the same script ran the checks. Regenerating them gives identical bytes. `queries.json` records the smallest relative gap between neighbouring distances among each query's top 11 (8.8e-06), so a reader can see no comparison turned on a near-tie at float32 precision.

Before any server was involved, the margin was also computed for nine other seeds (1025 to 1032 and 2026). None of them was meaningfully wider (all below 1e-4), so the seed was kept. This probe is not in `session.log`.

## What happened, in order

- 14:04 Environment and the pre-existing contents of the Docker host recorded (one container, two images, one volume, three networks).
- 14:05 Client installed: `pymilvus==3.0.2` (the version the vendor install page names) and NumPy, into a fresh venv. The dataset and queries were generated.
- 14:06 Compose file downloaded and read before running it: no privileged containers; a one-shot `milvus-volume-init` container runs as root to fix volume ownership and exits.
- 14:07:23 `docker compose up -d` started. It took 30 s including the image pulls.
- 14:08:04 `http://127.0.0.1:9091/healthz` returned 200 and `milvus-standalone` was healthy, at most 41 s after the start command. etcd and minio were healthy by 14:08:26. The health endpoint answered from Windows too.
- 14:08:34 **First client attempt.** The collection was created, 1,000 rows were inserted and flushed, then my script ran a `count(*)` query before loading the collection. Milvus answered "collection not loaded". That is a bug in the test script (the error is clear and correct), not a Milvus fault. The script was changed to read the count from the collection statistics before loading and from `count(*)` after, and committed.
- 14:09:11 **Second attempt.** Milvus did not answer for 180 s. The Ubuntu distro was stopped: WSL2 had shut the virtual machine down while no `wsl.exe` session was open (under about 40 s idle). That stopped Docker and the containers (etcd and minio exited with code 0, standalone with 255 as the engine came back). The data stayed on disk. This is an effect of running Docker Engine inside WSL2, not of Milvus. A background `wsl.exe -e sleep infinity` session was started to keep the engine up, and the containers were started again at 14:12:58. Whether the unplanned shutdown left the data intact was not checked: the next attempt drops and recreates the collection.
- 14:13:09 **Third attempt, `fresh` phase: PASS.** Insert count 1,000; the collection statistics said 1,000; `count(*)` said 1,000 after loading; the index was FLAT with the L2 metric; all 20 queries returned exactly the brute-force top 10, in the same order.
- 14:13:57 `docker compose restart` (etcd, minio and standalone). Healthy again by 14:14:42.
- 14:14:42 **`after-restart` phase: PASS.** The collection from before the restart was present, already loaded, FLAT index intact, 1,000 rows by both counts, and all 20 queries matched exactly again.
- 14:15:02 Teardown (below). 14:15:51 end of run.

## Result by scope key

- installation: verified. The documented Compose path produced a running, healthy stack, reachable from the client.
- ingest: verified. 1,000 inserted, 1,000 read back two ways.
- query: verified. 20 of 20 top-10 lists identical to the brute-force answer (ids and order) under a FLAT index.
- persistence: verified. Count, index and all 20 results survived a restart of the containers. A hard crash was not tested.
- teardown: verified, with two deviations from the docs (below). The Docker host was compared with the baseline taken first: same containers, images, volumes and networks.
- docsAccuracy: partial. The documented path worked, but several details differ from the pages as read. Findings:
  1. The docs say `sudo docker compose up -d`. It was run without sudo: the WSL user is in the docker group and sudo needs a password in this distro.
  2. The docs say the file "starts three containers". The file also defines a fourth, `milvus-volume-init`, a one-shot root container that fixes volume ownership and exits with code 0.
  3. The docs say the MinIO container serves ports 9000 and 9001 "locally", and their sample `docker compose ps` output shows no host mapping for them. The file publishes both on every interface of the WSL machine (0.0.0.0 and the IPv6 equivalent), with the default minio credentials. It was a short-lived local test and the containers were removed, but a reader following the docs would not expect it.
  4. The docs say to delete data with `sudo rm -rf volumes`. The folder is owned by root, and sudo needs a password here, so it was removed with a throwaway container run as root from the already pulled Milvus image. Any user without sudo hits the same wall.
  5. `docker compose down` left one anonymous Docker volume (created 29 s into the install, used by no container, cause not confirmed) that the docs do not mention. It was inspected and removed.
  6. The docs' only restart instruction is `docker restart milvus-standalone` after a config change. The spec asked for the containers, so `docker compose restart` restarted all three.
  7. The pages as read do not name `/healthz`; the check used the compose file's own healthcheck endpoint plus `docker compose ps`, which the docs do show.
  8. The pymilvus page also suggests installing `protobuf==3.20.0` and `grpcio-tools`. Neither was needed: `pymilvus==3.0.2` installed its own dependencies (protobuf 7.36.2) and worked on Python 3.14.7.
  9. The quickstart page uses Milvus Lite (a local file), not a server, and does not cover flush, load or counting rows. The connection examples elsewhere pass a token; this install accepted a connection without one.
  10. Compose prints a warning that the file's `version` attribute is obsolete.

## Teardown

`docker compose down` removed the containers and the network. The data folder was removed as described in finding 4, the three pulled images were removed, the working folder was removed, and the anonymous volume was removed. The Docker host then matched the baseline. The Ubuntu distro was stopped again (it was stopped before the test), which also stops the pre-existing gpustack container until the engine next starts.

## Not in `session.log`

The seed margin probe; the two git commits of the script (git blob `888d7a4d3c0a1a5d5dfa64582b8eb4d2860499da` for the first attempt, then `81a8cd8b9bedf0ef9fbd9d38dba8009cdbb3199c` for the attempts that count); deleting the first, empty venv before the logged install; deleting Python bytecode caches; starting and stopping the keep-alive process; and reading the vendor docs. One earlier environment probe was discarded because a quoting error mangled one command; it only listed versions and the pre-existing containers.

## Not tested

Kubernetes or distributed Milvus; the single-container `standalone_embed.sh` path and the Windows batch path (both documented, neither run); any index other than FLAT; GPU indexes; more than 1,000 vectors; concurrent clients; authentication and access control; backup and restore; upgrades; recovery after a hard crash; Zilliz Cloud. Timings appear in `session.log` for completeness and are not a result: this was a correctness check.
