Как установить и настроить Milvus на VPS
Milvus обещает миллиарды векторов и субсекундный поиск, а начинается с того, что вместо одного бинарника — три контейнера, и один из них etcd. Установка на VPS упирается не в команду запуска, а в память под индекс, неизменяемую размерность коллекции и порты, которые официальный compose выставляет наружу без пароля. Разберём путь: подготовка сервера, standalone в Docker, первая коллекция, расчёт RAM, авторизация, бэкап.
Содержание
- Что такое Milvus и почему установка сложнее, чем у Qdrant
- Подготовка VPS: Docker, диск и лимиты
- Установка standalone: скрипт за минуту или compose для боя
- Первая коллекция: схема, размерность и индекс
- Память под индекс: расчёт и способы ужаться
- Безопасность, бэкап и обновление
- Какой сервер под Milvus брать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Milvus и почему установка сложнее, чем у Qdrant
Milvus — векторная СУБД под лицензией Apache 2.0: ядро на Go, поиск — Knowhere на C++. От Qdrant и Chroma её отличает не API, а устройство: распределённая система из ролей — proxy, координаторы, query/data/index-ноды, а standalone сворачивает их в один процесс. Отсюда: Milvus — это всегда минимум три сервиса, если не выбран Lite.
| Режим | Что это на самом деле | Кому подходит |
|---|---|---|
| Milvus Lite | библиотека pymilvus, база в файле | прототип, до миллиона векторов |
| Standalone | Milvus + etcd + MinIO, три контейнера | 90% задач на VPS, десятки миллионов |
| Distributed | Kubernetes, Helm-чарт | шардирование, раздельное масштабирование |
Lite сервера не поднимает вообще:
from pymilvus import MilvusClient
client = MilvusClient("./milvus_demo.db") # это Milvus Lite, а не сервер
Собирается под Linux и macOS — без авторизации, сети и части индексов.
Порты связки:
| Компонент | Порт | Зачем |
|---|---|---|
| Milvus | 19530 | gRPC-API + RESTful v2 (cmux) |
| Milvus | 9091 | /healthz, /metrics |
| etcd | 2379 | метаданные: схемы, сегменты, узлы |
| MinIO | 9000 / 9001 | сегменты, бинлоги, индексы, консоль |
Очередь сообщений встроенная — RocksMQ в /var/lib/milvus/rdb_data, Pulsar и Kafka нужны только распределённой сборке. Документация просит минимум 4 ядра, 8 ГБ RAM и 100 ГБ диска, рекомендует 8/32 — коллекция грузится в RAM целиком, это не перестраховка.
Общая для векторных баз граница: Milvus не считает эмбеддинги, генерация есть только для разреженного BM25. Как считать векторы без видеокарты — в статье про эмбеддинги на CPU.
Подготовка VPS: Docker, диск и лимиты
Ubuntu 24.04 или Debian 12, SSH под пользователем с sudo. Milvus требует Docker 19.03+ и Docker Compose v2:
curl -fsSL https://get.docker.com | sh
docker --version && docker compose version
Три проверки дешевле до установки, чем после.
Свободные порты. Если на 9000 уже живёт MinIO, контейнер не поднимется: Bind for 0.0.0.0:9000 failed: port is already allocated.
ss -ltnp | grep -E '19530|9091|2379|9000|9001'
Скорость диска — главный неочевидный пункт: etcd пишет каждую транзакцию с fsync, на медленном диске Milvus разваливается под нагрузкой без понятной ошибки. Разработчики etcd рекомендуют 99-й перцентиль fsync ниже 10 мс — проверяется их тестом:
sudo apt install -y fio
fio --rw=write --ioengine=sync --fdatasync=1 --directory=/var/lib/docker \
--size=200m --bs=2300 --name=etcd-fsync
Смотрите 99.00th в блоке fsync/fdatasync/sync_file_range: на NVMe — единицы миллисекунд, на перегруженном SATA — десятки и сотни. В бою: apply request took too long в логе etcd, Milvus падает на context deadline exceeded. На HDD Milvus ставить не надо.
Память и swap. Своп векторную базу не спасает: промах в подкачанный индекс растит задержку на порядки, а etcd от паузы отваливается по таймауту — подробнее, что делать при нехватке RAM. Заодно поднимите лимит дескрипторов: ulimits: nofile: { soft: 65536, hard: 65536 }.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверУстановка standalone: скрипт за минуту или compose для боя
Путь первый — официальный скрипт: один контейнер, Milvus со встроенным etcd (ETCD_USE_EMBED=true) и локальным хранилищем вместо MinIO (COMMON_STORAGETYPE=local).
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
bash standalone_embed.sh start
Рядом появятся volumes/milvus, embedEtcd.yaml, user.yaml; скрипт понимает start/stop/delete/upgrade. Подвох: публикует -p 2379:2379 — etcd со всеми метаданными доступен без пароля. Проверьте ss -ltnp | grep 2379 и закройте порт.
Путь второй — Docker Compose, так ставят на бой. Официальный файл с нужным тегом публикует наружу 19530, 9000 и 9001:
wget https://github.com/milvus-io/milvus/releases/download/v2.5.10/milvus-standalone-docker-compose.yml -O docker-compose.yml
Правленый вариант — всё на петле:
services:
etcd:
container_name: milvus-etcd
image: quay.io/coreos/etcd:v3.5.18
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_SNAPSHOT_COUNT=50000
volumes:
- ./volumes/etcd:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck:
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s
timeout: 20s
retries: 3
minio:
container_name: milvus-minio
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
environment:
MINIO_ROOT_USER: milvus
MINIO_ROOT_PASSWORD: ЗАМЕНИТЕ_МЕНЯ
ports:
- "127.0.0.1:9001:9001"
volumes:
- ./volumes/minio:/minio_data
command: minio server /minio_data --console-address ":9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
standalone:
container_name: milvus-standalone
image: milvusdb/milvus:v2.5.10
command: ["milvus", "run", "standalone"]
security_opt:
- seccomp:unconfined
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
MINIO_ACCESS_KEY_ID: milvus
MINIO_SECRET_ACCESS_KEY: ЗАМЕНИТЕ_МЕНЯ
volumes:
- ./volumes/milvus:/var/lib/milvus
- ./user.yaml:/milvus/configs/user.yaml
ports:
- "127.0.0.1:19530:19530"
- "127.0.0.1:9091:9091"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s
start_period: 90s
timeout: 20s
retries: 3
depends_on: [etcd, minio]
attu:
image: zilliz/attu:v2.5
environment:
MILVUS_URL: milvus-standalone:19530
ports:
- "127.0.0.1:8000:3000"
Четыре строки нельзя выкидывать: ETCD_QUOTA_BACKEND_BYTES поднимает лимит etcd с 2 до 4 ГиБ (без него — etcdserver: mvcc: database space exceeded, и Milvus встаёт целиком), ETCD_AUTO_COMPACTION_* уплотняет ревизии, иначе лимит выбирается за недели, seccomp:unconfined нужен Milvus — дефолт блокирует часть системных вызовов, start_period: 90s — холодный старт больше минуты.
mkdir -p volumes && touch user.yaml
docker compose up -d && docker compose ps
curl -s http://127.0.0.1:9091/healthz
Здоровая система отвечает OK, docker compose ps — Up ... (healthy). Restarting — смотрите docker compose logs --tail=80 standalone: dial tcp: lookup minio on 127.0.0.11:53: no such host — разные сети или не встал MinIO; context deadline exceeded у etcd — сам не стартовал, чаще из-за прав на ./volumes/etcd или места на диске. Веб-интерфейс Attu — на туннеле ssh -N -L 8000:127.0.0.1:8000 user@server.
Первая коллекция: схема, размерность и индекс
Клиент не должен быть старше сервера:
pip install -U pymilvus
python3 - <<'PY'
from pymilvus import connections, utility
connections.connect(uri="http://127.0.0.1:19530")
print(utility.get_server_version())
PY
Ответ v2.5.10 значит, что сервер жив. Вместо версии — MilvusException: (code=2, message=Fail connecting to server on 127.0.0.1:19530, illegal connection params or server unavailable): контейнер не поднялся либо publish на localhost, а стучитесь снаружи.
Создавайте коллекцию явно, а не через create_collection("docs", dimension=768) из быстрого старта — со схемой видно все решения:
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://127.0.0.1:19530")
schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("vector", DataType.FLOAT_VECTOR, dim=1024)
schema.add_field("text", DataType.VARCHAR, max_length=8192)
schema.add_field("lang", DataType.VARCHAR, max_length=8)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="vector", index_type="HNSW", metric_type="COSINE",
params={"M": 16, "efConstruction": 200},
)
client.create_collection("docs", schema=schema, index_params=index_params)
client.load_collection("docs")
Что решается навсегда:
dimизменить нельзя — сменили модель эмбеддингов, заводите новую коллекцию. Ориентиры:bge-m3— 1024,text-embedding-3-small— 1536; несовпадение —the dim (768) of field data(vector) is not equal to schema dim (1024).metric_typeдолжен соответствовать модели:COSINE/IPдля нормализованных векторов,L2для остальных — перепутали, и поиск молча вернёт мусор, что хуже падения.- Без индекса и
load_collectionпоиск не работает — незагруженная коллекция вернётcollection not loaded. Не хотите выбирать параметры —index_type="AUTOINDEX", за ним тот же HNSW.
Две ловушки на вставке: Milvus не проверяет уникальность id — повтор insert даёт второй документ и дубли в поиске, для перезаливки нужен upsert(...). Свежее находится не сразу: consistency по умолчанию Bounded, между вставкой и видимостью — секунды; в тестах — consistency_level="Strong", в бою оставьте как есть — дороже по задержке.
client.insert("docs", [{"id": 1, "vector": vec, "text": "…", "lang": "ru"}])
res = client.search("docs", data=[query_vec], limit=5,
search_params={"params": {"ef": 64}},
filter='lang == "ru"', output_fields=["text"])
Память под индекс: расчёт и способы ужаться
Milvus держит загруженную коллекцию в RAM целиком, поэтому объём памяти определяет не число документов, а размерность векторов. float32 — 4 байта, вектор — dim × 4: для 1024 измерений это 4096 байт, миллион векторов — 4,1 ГБ сырых данных; граф HNSW при M = 16 добавляет ~0,13 ГБ на миллион (M × 2 × 4 байт на элемент).
| Модель | dim | Вектор | 1 млн | 5 млн |
|---|---|---|---|---|
| bge-small | 384 | 1,5 КБ | 1,4 ГБ | 7,2 ГБ |
| e5-base / open-source | 768 | 3,0 КБ | 2,9 ГБ | 14,3 ГБ |
| bge-m3 | 1024 | 4,0 КБ | 3,8 ГБ | 19,1 ГБ |
| text-embedding-3-large | 3072 | 12,0 КБ | 11,4 ГБ | 57,2 ГБ |
Это оценка объёма данных, а не замер процесса: прибавляйте payload и запас на слияние сегментов. Отсюда документированные 8 ГБ минимума — на них живёт около миллиона 768-мерных векторов, не больше. Когда цифры не сходятся, есть три рычага с честной ценой у каждого.
- mmap. С версии 2.4 Milvus умеет отображать векторы с диска вместо RAM; ключ жил под
queryNode.mmap, в 2.5 стал верхнеуровневым. Цена — диск в горячем пути: терпимо на NVMe, неприемлемо на сетевом хранилище.
mmap:
mmapEnabled: true
vectorField: true
- Квантование.
IVF_SQ8сжимаетfloat32доint8, уменьшая объём вчетверо,IVF_PQ— сильнее; recall падает, насколько — зависит от данных, проверяйте на своей выборке. - DISKANN. Индекс на диске, в памяти — сжатое представление. Требует
queryNode.enableDisk: trueи локальный NVMe.
При переполнении срабатывает защита: quotaAndLimits.limitWriting.memProtection задаёт пороги queryNodeMemoryHighWaterLevel: 0.95 и dataNodeMemoryHighWaterLevel: 0.95, запись отклоняется с quota exceeded[reason=memory quota exceeded] — хороший исход. Плохой — sudo dmesg -T | grep -i 'killed process' со строкой milvus: сервер меньше коллекции. Расчёт с другими индексами — в материале про RAM для векторной базы.
Безопасность, бэкап и обновление
Авторизации по умолчанию нет — кто дотянулся до 19530, тот читает и удаляет коллекции. Включается через user.yaml, смонтированный в /milvus/configs/user.yaml:
common:
security:
authorizationEnabled: true
После docker compose restart standalone появляется root с паролем Milvus, одинаковым у всех установок. Меняйте сразу; для приложения — отдельный пользователь через create_user/create_role/grant_privilege.
client = MilvusClient(uri="http://127.0.0.1:19530", token="root:Milvus")
client.update_password(user_name="root", old_password="Milvus", new_password="…")
MinIO с дефолтными ключами. В compose из релиза — minioadmin:minioadmin, консоль на 9001 публикуется наружу. Меняйте до первого запуска — после придётся синхронно править minio.accessKeyID и minio.secretAccessKey, иначе Milvus не увидит бакет.
Фаервол и Docker. -p 19530:19530 обходит ufw — Docker пишет свои правила DNAT, порт открыт при любых ufw deny. Надёжно — петля, как выше; ufw allow 22/tcp, ufw deny 19530/tcp и 2379/tcp вторым рубежом.
Доступ снаружи. Для проверок хватит ssh -N -L 19530:127.0.0.1:19530 user@server. Постоянному — reverse-proxy с TLS и gRPC: proxy_pass Nginx не годится, нужен grpc_pass с HTTP/2 — в Ubuntu 24.04 версия Nginx 1.24, а http2 on; заработала только с 1.25.1. Либо TLS Milvus напрямую — tlsMode: 1.
Бэкап. Архивировать volumes/ на работающей базе нельзя — etcd и MinIO снимутся в разные моменты, восстановление даст коллекцию с потерянными сегментами. Холодный путь: docker compose stop, архив, старт обратно. Горячий — milvus-backup от Zilliz:
./milvus-backup check
./milvus-backup create -n backup_2026_08
./milvus-backup restore -n backup_2026_08 -s _restored
check подтверждает, что configs/backup.yaml смотрит на тот же MinIO и bucket — частая ошибка настройки. Восстановление создаёт коллекции с суффиксом docs_restored, не затирая живые.
Обновление. Внутри минорной ветки — сменить тег и docker compose up -d. Между версиями — release notes: миграция метаданных бывает односторонней, понижение версии официально не поддерживается. Порядок один: бэкап, снимок диска, смена тега.
Какой сервер под Milvus брать в MAATRIX
Сразу честно: Milvus нет в каталоге apps.maatrix.io, автоустановки не будет. Сервер приезжает чистым — Ubuntu 24.04 или Debian 12, — ставите по третьему разделу, минут за пятнадцать. В каталоге есть готовые сборки соседей: Qdrant, Ollama, Open WebUI и AnythingLLM как интерфейсы, LiteLLM — шлюз к внешним API, Portainer для контейнеров.
Честный минимум: 4 vCPU, 8 ГБ RAM, 100 ГБ NVMe — то, что просит документация, и не зря. На 2–4 ГБ standalone стартует, отвечает OK на /healthz, но первый load_collection на миллионе векторов кончается записью в dmesg. На 8 ГБ спокойно живёт около миллиона 768-мерных векторов. Корпус — десятки тысяч документов, растёт медленно? Разумнее Qdrant: один контейнер, вдвое больше свободной памяти на том же железе.
Комфортный вариант: 8 vCPU, 32 ГБ RAM, 200–300 ГБ NVMe — рекомендация разработчиков, точка, где Milvus окупает сложность: 5–10 млн векторов на 768 измерениях без mmap, место под MinIO, бэкапы, вторую коллекцию под новую модель. Для 3072-мерных — 64 ГБ либо сразу mmap и DISKANN. Диск — с коэффициентом три: MinIO, индексы, WAL RocksMQ (retentionSizeInMB: 8192, трое суток), бэкапы. Только NVMe — из-за fsync etcd.
Локация — Великобритания, Лондон. Milvus редко стоит один: рядом модель эмбеддингов, LLM-шлюз, приложение. С британского адреса Hugging Face, PyPI, GitHub и реестры контейнеров открываются напрямую, без зеркал и обрывов на середине образа, пинг до Европы — единицы-десятки миллисекунд. US — если рядом прокси к OpenAI/Anthropic, RU — если данные обязаны оставаться в РФ по 152-ФЗ. Схема связки — в статье про RAG по своим документам.
Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT; иностранная карта не нужна, доступы — в личном кабинете. Перед заказом посчитайте dim × 4 × число_векторов — арифметика на салфетке точнее тарифного калькулятора, сразу покажет, нужен ли Milvus или хватит базы попроще.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Можно ли поднять Milvus на VPS с 2 ГБ RAM?
Серверный standalone — нет: три контейнера и загруженная коллекция не поместятся, dmesg покажет убитый процесс. На 2 ГБ — только Milvus Lite внутри Python-процесса, без сети и авторизации.
Вставил векторы, а поиск их не находит. База сломалась?
Почти наверняка нет. Проверьте: выполнен ли load_collection (иначе collection not loaded), есть ли индекс на векторном поле, не мешает ли Bounded consistency по умолчанию — записи видны с задержкой в секунды. Для проверки передайте consistency_level="Strong".
Milvus упал после недель работы, в логах mvcc: database space exceeded. Что это?
Переполнилась база etcd: лимит по умолчанию 2 ГиБ, без автоуплотнения выбирается активной записью — отсюда ETCD_QUOTA_BACKEND_BYTES и ETCD_AUTO_COMPACTION_* в compose. Лечится etcdctl defrag в контейнере etcd, затем флаг NOSPACE снимают через etcdctl alarm disarm.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.