MAATRIX / Блог / Как установить и настроить Weaviate на VPS

Как установить и настроить Weaviate на VPS

Как установить и настроить Weaviate на VPS

MAATRIX

Weaviate поднимается одной командой docker compose up -d, и девять инструкций из десяти на этом заканчиваются. Через неделю выясняется, что база слушает мир без пароля, питоновский клиент не подключается из-за закрытого порта 50051, а диск заполнился на 91 % — и все шарды молча ушли в READONLY. Разберём установку Weaviate на VPS целиком: развёртывание, ключи и Nginx, схему класса, память, бэкапы и обновление.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что такое Weaviate и какие порты он открывает

Weaviate — открытая векторная база на Go: один процесс, без JVM и внешних сервисов рядом. Хранит объекты — UUID, JSON-свойства и векторы при них.

Первая грабля — имена. В REST API сущность называется class, в клиентах v4 — коллекцией, и имя обязано начинаться с заглавной буквы: "class": "docs" база молча превратит в Docs, а { Get { docs { text } } } ответит Cannot query field "docs" on type "GetObjectsObj". Свойства — только со строчной, по маске [_A-Za-z][_0-9A-Za-z]*.

Вторая — векторизатор: text2vec-openai, text2vec-ollama, text2vec-transformers считают эмбеддинги сами, модуль задаётся на уровне класса. Приносите готовые векторы, а DEFAULT_VECTORIZER_MODULE не выставлен — вставка упадёт с no module with name "text2vec-openai" present. Правильное значение — none.

ПортЧто этоНаружу
8080REST API, GraphQLчерез reverse proxy с TLS
50051gRPCобязателен клиентам v4, тоже через proxy
2112метрики Prometheusнет, внутренняя сеть
7100, 7101, 8300, 8301gossip и Raft узловнет, только в кластере

Порт 50051 не опция: с четвёртой ветки питоновского клиента поиск и батчи идут по gRPC, и при открытом только 8080 подключение упадёт на первом же запросе. Живость проверяют без авторизации на /v1/.well-known/ready, версию — на /v1/meta:

curl -s http://127.0.0.1:8080/v1/meta | jq
{
  "hostname": "http://[::]:8080",
  "modules": {},
  "version": "1.32.0"
}

Установка: Docker Compose или бинарник под systemd

Сразу честно: Weaviate нет в каталоге приложений apps.maatrix.io — сервер приезжает чистым, базу ставим руками, это пятнадцать минут. В каталоге есть родственные сервисы (Qdrant, Ollama, AnythingLLM, Dify); нужна векторная база «из коробки» — смотрите установку Qdrant.

Docker Compose предпочтительнее: образ собирают разработчики, обновление — смена тега. Файл /opt/weaviate/compose.yaml:

services:
  weaviate:
    image: cr.weaviate.io/semitechnologies/weaviate:1.32.0
    container_name: weaviate
    restart: unless-stopped
    command: ["--host", "0.0.0.0", "--port", "8080", "--scheme", "http"]
    ports:
      - "127.0.0.1:8080:8080"
      - "127.0.0.1:50051:50051"
    volumes:
      - /var/lib/weaviate:/var/lib/weaviate
      - /var/backups/weaviate:/var/backups/weaviate
    environment:
      PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
      DEFAULT_VECTORIZER_MODULE: 'none'
      ENABLE_MODULES: 'backup-filesystem'
      BACKUP_FILESYSTEM_PATH: '/var/backups/weaviate'
      CLUSTER_HOSTNAME: 'node1'
      QUERY_DEFAULTS_LIMIT: 25
      AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false'
      AUTHENTICATION_APIKEY_ENABLED: 'true'
      AUTHENTICATION_APIKEY_ALLOWED_KEYS: 'REPLACE_ADMIN_KEY,REPLACE_RO_KEY'
      AUTHENTICATION_APIKEY_USERS: 'admin@example.com,reader@example.com'
      AUTHORIZATION_ADMINLIST_ENABLED: 'true'
      AUTHORIZATION_ADMINLIST_USERS: 'admin@example.com'
      AUTHORIZATION_ADMINLIST_READONLY_USERS: 'reader@example.com'
      DISK_USE_WARNING_PERCENTAGE: '80'
      DISK_USE_READONLY_PERCENTAGE: '90'
      GOMEMLIMIT: '3GiB'
      LOG_LEVEL: 'info'

Четыре места, куда стоит присмотреться.

  • Тег версии. :latest в проде — мина: перезапуск через месяц подтянет другую минорную версию без отката назад.
  • Публикация портов. "8080:8080" без адреса открывает все интерфейсы: Docker пишет правило в цепочку DOCKER таблицы nat, минуя ufwufw status покажет порт закрытым, а база видна интернету.
  • Том с данными. Без тома унесёт базу docker compose down, с томом — down -v. Каталог бэкапов монтируйте отдельно от каталога данных, иначе бэкап начнёт копировать сам себя.
  • CLUSTER_HOSTNAME. Задайте явно и не трогайте: с 1.25 схема хранится в Raft-логе с именем узла внутри, после переименования контейнера узел может не найти себя.
mkdir -p /var/lib/weaviate /var/backups/weaviate
docker compose up -d
docker compose logs weaviate | jq -r '.msg' | head -20

Ищите строку Serving weaviate at http://[::]:8080 — HTTP-слушатель поднялся. Логи по умолчанию в JSON, отсюда jq; для аварий ставьте LOG_LEVEL: 'debug', для чтения глазами — LOG_FORMAT: 'text'.

Без Docker. Бинарники есть на GitHub: файл в /opt/weaviate, настройки — через systemd EnvironmentFile.

[Unit]
Description=Weaviate vector database
After=network-online.target

[Service]
User=weaviate
Group=weaviate
EnvironmentFile=/etc/weaviate/weaviate.env
ExecStart=/opt/weaviate/weaviate --host 127.0.0.1 --port 8080 --scheme http
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

LimitNOFILE не для красоты: на дефолтном лимите дескрипторов большая коллекция даёт too many open files посреди заливки. И учтите: флаг --host — только для REST, порт gRPC задаёт GRPC_PORT и слушает все интерфейсы; без Docker закрывайте его вручную — ufw deny 50051/tcp.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Закрываем доступ: API-ключи, RBAC и Nginx с gRPC

По умолчанию AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED включён — свежая база пускает кого угодно на чтение и запись. Проверка с другой машины: curl -m 5 -s http://ВАШ_IP:8080/v1/schema — JSON вместо таймаута значит, что базу видно всему интернету и её можно выкачать или снести DELETE.

Правило, из-за которого аутентификация чаще всего «не работает»: AUTHENTICATION_APIKEY_ALLOWED_KEYS и AUTHENTICATION_APIKEY_USERSдва списка одинаковой длины по порядку, разъехались — ключ достаётся не тому. Ключи — openssl rand -hex 32. Анонимный доступ без схемы аутентификации не отключить — процесс не стартует; запрос без ключа вернёт 401 {"error":[{"message":"anonymous access not enabled"}]}.

export WEAVIATE_API_KEY='ваш-ключ'
curl -s -H "Authorization: Bearer $WEAVIATE_API_KEY" \
     http://127.0.0.1:8080/v1/schema | jq '.classes[].class'

AUTHORIZATION_ADMINLIST_* — два уровня: полные права и только чтение. С версии 1.29 есть RBAC с ролями на отдельные коллекции (AUTHORIZATION_RBAC_ENABLED, AUTHORIZATION_RBAC_ROOT_USERS), обе модели разом не включают.

Наружу базу выпускают через Nginx: два протокола на двух портах — REST проксируется обычным proxy_pass, gRPC требует grpc_pass и HTTP/2. Один серверный блок закрывает оба:

server {
    listen 443 ssl;
    http2 on;
    server_name vec.example.com;

    ssl_certificate     /etc/letsencrypt/live/vec.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vec.example.com/privkey.pem;

    location /weaviate.v1.Weaviate/ {
        grpc_pass grpc://127.0.0.1:50051;
        grpc_read_timeout 300s;
        grpc_send_timeout 300s;
    }

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_read_timeout 300s;
        client_max_body_size 64m;
    }
}

/weaviate.v1.Weaviate/ — полное имя gRPC-сервиса, по нему идут все вызовы клиента. client_max_body_size поднят не зря: батч на сотни объектов по 1536 измерений перевалит дефолтный мегабайт, Nginx ответит 413 Request Entity Too Large раньше базы. Клиент подключают с обоими хостами на 443:

import weaviate
from weaviate.classes.init import Auth

client = weaviate.connect_to_custom(
    http_host="vec.example.com", http_port=443, http_secure=True,
    grpc_host="vec.example.com", grpc_port=443, grpc_secure=True,
    auth_credentials=Auth.api_key("ваш-ключ"),
)
print(client.is_ready())
client.close()

Первая коллекция: схема, размерность и типы индексов

Схема создаётся одним POST. Боевой пример для базы знаний, где векторы считаются снаружи:

curl -s -X POST http://127.0.0.1:8080/v1/schema \
  -H "Authorization: Bearer $WEAVIATE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "class": "Docs",
    "vectorizer": "none",
    "vectorIndexType": "hnsw",
    "vectorIndexConfig": {
      "distance": "cosine", "efConstruction": 128,
      "ef": -1, "dynamicEfMin": 100, "dynamicEfMax": 500
    },
    "properties": [
      {"name": "text",   "dataType": ["text"], "indexSearchable": true},
      {"name": "source", "dataType": ["text"], "indexFilterable": true, "indexSearchable": false, "tokenization": "field"},
      {"name": "chunk",  "dataType": ["int"],  "indexFilterable": true}
    ]
  }'

Размерность вектора нигде не указана — Weaviate запоминает её по первому объекту, удобно до смены модели: с 768 измерений на 1536 — вставка вернёт vector lengths don't match: 768 vs 1536. Ни размерность, ни distance у класса не поменять — только новый класс и переиндексация. Метрику берут под модель: cosine для нормализованных эмбеддингов, dot — для скалярного произведения; ошибка молчаливая, портит выдачу.

indexFilterable и indexSearchable — разные вещи. Первый — индекс для фильтров вроде where: {path:["source"], operator: Equal, valueText:"handbook.md"}, второй — BM25 для полнотекста. Полю source нужен только первый: выключенный indexSearchable экономит диск и память.

Массовую заливку делают через /v1/batch/objects — вторая классическая грабля: эндпоинт отвечает 200 OK, даже если не вставился ни один объект, ошибки лежат в поле result каждого элемента и разбираются отдельно.

curl -s -X POST http://127.0.0.1:8080/v1/batch/objects \
  -H "Authorization: Bearer $WEAVIATE_API_KEY" -H 'Content-Type: application/json' \
  -d @batch.json | jq '[.[] | select(.result.errors != null) | .result.errors.error[0].message] | unique'

Поиск по вектору идёт через GraphQL:

curl -s http://127.0.0.1:8080/v1/graphql \
  -H "Authorization: Bearer $WEAVIATE_API_KEY" -H 'Content-Type: application/json' \
  -d '{"query":"{ Get { Docs(nearVector: {vector: [0.013,-0.041,0.220]}, limit: 3, where: {path:[\"source\"], operator: Equal, valueText: \"handbook.md\"}) { text source _additional { distance id } } } }"}'

Гибридный поиск, ради которого Weaviate часто и берут: hybrid смешивает BM25 и векторную близость, alpha задаёт баланс (0 — чистый BM25, 1 — чистые векторы, по умолчанию 0,75; слияние с 1.24 — relativeScoreFusion). Для схемы с vectorizer: none вектор передают явно: hybrid: {query: "отпуск", vector: [...], alpha: 0.6} — без vector гибрид станет обычным полнотекстом.

Память и диск: сколько Weaviate просит и чем это ужать

Weaviate держит индекс HNSW в оперативной памяти — это главный фактор при выборе сервера. Ориентир из документации: память ≈ 2 × число объектов × размерность × 4 байта, где 4 байта — float32, а двойка закрывает граф связей и служебные структуры.

ОбъектовРазмерностьВекторыС индексом (×2)
100 0003840,15 ГБ≈ 0,3 ГБ
500 0007681,5 ГБ≈ 3 ГБ
1 000 0007683,1 ГБ≈ 6,1 ГБ
1 000 00015366,1 ГБ≈ 12,3 ГБ

Сверху ложатся ОС, page cache и индексирующий скрипт. Вывод: миллион фрагментов на text-embedding-3-small (1536 измерений) без квантования требует 16 ГБ — это рабочее требование, не запас на будущее.

GOMEMLIMIT ставьте всегда. Go-сборщик не знает объём памяти машины и разгоняет кучу до OOM-killer. Значение — около 80% RAM: '3GiB' на четырёх гигабайтах, '12GiB' на шестнадцати. LIMIT_RESOURCES: 'true' делает то же само, но перебивает ручной GOMEMLIMIT.

Квантование — главный рычаг экономии. Скалярное (sq) ужимает вектор до int8 вчетверо, почти без потери качества. Бинарное (bq) — бит на измерение, сжатие в 32 раза (миллион векторов на 768 измерений: 3,1 ГБ → 96 МБ), но требует пересчёта кандидатов (rescoreLimit) и теряет полноту на 384 измерениях. Продуктовое (pq) требует обучения на данных — начинайте со sq.

Про диск: DISK_USE_WARNING_PERCENTAGE и DISK_USE_READONLY_PERCENTAGE не декорация — выше 90% лог получит disk usage currently at 91.03%, threshold set to 90.00%, шарды уйдут в READONLY. Приложение не падает, но получает отказы на запись, а данные тихо теряются. Освободите место или форсируйте вручную:

curl -s -X PUT http://127.0.0.1:8080/v1/schema/Docs/shards/ШАРД \
  -H "Authorization: Bearer $WEAVIATE_API_KEY" -H 'Content-Type: application/json' \
  -d '{"status":"READY"}'

Локальная модель эмбеддингов конкурирует с базой за память. На нашем стенде (AMD EPYC 9554, 16 vCPU, Ollama 0.33.1, qwen2.5:7b в Q4_K_M) генерация выходит на полку уже на четырёх потоках — 5,7 токена в секунду на двух, 7,6 на четырёх и на восьми: упирается не в ядра, а в память, тот же ресурс, что нужен HNSW. Эмбеддер рядом с базой на 2 vCPU/4 ГБ не ставьте — способы считать векторы дешевле в материале про эмбеддинги на CPU без GPU.

Эксплуатация: бэкапы, обновления и наблюдение

Копировать /var/lib/weaviate на живой базе бесполезно — получите несогласованный снимок LSM-хранилища. Штатный механизм есть, но модуль должен быть включён с самого начала: добавление backup-filesystem в ENABLE_MODULES требует рестарта.

curl -s -X POST http://127.0.0.1:8080/v1/backups/filesystem \
  -H "Authorization: Bearer $WEAVIATE_API_KEY" -H 'Content-Type: application/json' \
  -d '{"id":"2026-08-28-nightly","include":["Docs"]}'

curl -s -H "Authorization: Bearer $WEAVIATE_API_KEY" \
     http://127.0.0.1:8080/v1/backups/filesystem/2026-08-28-nightly | jq '.status'

Идентификатор — только строчные буквы, цифры, дефисы, подчёркивания: Nightly-2026 отвергнут. Статус: STARTEDTRANSFERRINGSUCCESS, операция асинхронная — нужен опрос в cron. Восстановление — путь с /restore, но существующий класс Weaviate не восстановит, сперва удаляют. Честный минус: архив на том же диске, где база — от порчи данных спасает, от смерти диска нет. Копии наружу — rsync или backup-s3 (BACKUP_S3_BUCKET, BACKUP_S3_ENDPOINT).

Состояние узла — /v1/nodes?output=verbose: имя, статус HEALTHY, объекты и шарды. Нужны графики — PROMETHEUS_MONITORING_ENABLED: 'true', метрики на порт 2112 (наружу не публиковать); смотрите на heap и startup_progress — он отличает зависшую базу от загружающейся.

Обновление — смена тега, docker compose pull && docker compose up -d. Три правила: одна минорная версия за раз; читать release notes — миграции формата случаются (переход на 1.25 с Raft-хранилищем был именно таким); бэкап до обновления обязателен — откат не поддерживается.

Какой сервер под Weaviate взять в MAATRIX

Считайте по формуле выше: удвоенный объём векторов плюс запас на ОС и индексирующий скрипт. 200 000 фрагментов при 768 измерениях — 0,6 ГБ индекса, скромная машина; миллион фрагментов при 1536 — уже 12,3 ГБ, и это не отменить ничем, кроме квантования.

Честный минимум: 2 vCPU, 4 ГБ RAM, 60 ГБ NVMe. Хватает на 300–400 тысяч фрагментов умеренной размерности, Nginx с TLS и бэкапы рядом. Ограничение прямо: локальные эмбеддинги здесь не считать — sentence-transformers заберёт два-три гигабайта, и OOM-killer выберет не в вашу пользу. На 2 ГБ заливка кончится кодом 137, "OOMKilled": true в docker inspect.

Комфортный вариант: 4 vCPU, 8–16 ГБ RAM, 120–200 ГБ NVMe. Помещаются миллион векторов, локальный эмбеддер, гибридный поиск и бэкапы объёмом с саму базу — диск с двойным запасом. Четыре ядра дают параллельные запросы и фоновую сборку графа; дальше растут по памяти — HNSW упирается в RAM раньше процессора.

Локация — UK, Лондон. Пинг до европейских пользователей и сервисов в ЕС — десятки миллисекунд, а RAG-конвейер ходит в базу не раз за ответ. С британского адреса штатно отвечают API OpenAI, Cohere и Voyage — скрипт не упрётся в региональные ограничения, из-за которых российская локация тут не годится. Плюс GDPR-соседство для клиентов из ЕС.

Заказ — несколько минут: локация, конфигурация, чистая Ubuntu 24.04 или Debian 12, дальше шаги этой статьи с docker compose up -d. В каталоге apps.maatrix.io Weaviate нет, зато есть Qdrant, Ollama, AnythingLLM, Dify — соседние кубики ставятся автоматически, базу добавляете сами (подробнее — в обзоре векторной базы для RAG). Оплата — картами российских банков, СБП, криптой или MAAT, даже для сервера в Лондоне.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Можно ли поменять размерность вектора или метрику расстояния у существующего класса?

Нет. Размерность фиксируется первым объектом, метрика — при создании класса, обе неизменяемы. Вставка другой длины вернёт vector lengths don't match: 768 vs 1536. Путь один: новый класс и переиндексация.

Обязательно ли открывать порт 50051, если я работаю только через REST?

Для curl и GraphQL — нет, всё на 8080. Но питоновский клиент v4 использует gRPC для поиска и батчей и без порта не подключится. За Nginx проксируйте оба: grpc_pass на /weaviate.v1.Weaviate/, proxy_pass на остальное.

База перестала принимать записи, но приложение ошибок не показывает. Что случилось?

Скорее всего, шарды ушли в READONLY по месту на диске: df -h и строка disk usage currently at в логе. Вторая причина — батчевая вставка, где ошибки лежат внутри ответа 200: jq '[.[] | select(.result.errors != null)]'.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.