Как установить и настроить Weaviate на VPS
Weaviate поднимается одной командой docker compose up -d, и девять инструкций из десяти на этом заканчиваются. Через неделю выясняется, что база слушает мир без пароля, питоновский клиент не подключается из-за закрытого порта 50051, а диск заполнился на 91 % — и все шарды молча ушли в READONLY. Разберём установку Weaviate на VPS целиком: развёртывание, ключи и Nginx, схему класса, память, бэкапы и обновление.
Содержание
- Что такое Weaviate и какие порты он открывает
- Установка: Docker Compose или бинарник под systemd
- Закрываем доступ: API-ключи, RBAC и Nginx с gRPC
- Первая коллекция: схема, размерность и типы индексов
- Память и диск: сколько Weaviate просит и чем это ужать
- Эксплуатация: бэкапы, обновления и наблюдение
- Какой сервер под Weaviate взять в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество 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.
| Порт | Что это | Наружу |
|---|---|---|
| 8080 | REST API, GraphQL | через reverse proxy с TLS |
| 50051 | gRPC | обязателен клиентам v4, тоже через proxy |
| 2112 | метрики Prometheus | нет, внутренняя сеть |
| 7100, 7101, 8300, 8301 | gossip и 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, минуя ufw —ufw 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 000 | 384 | 0,15 ГБ | ≈ 0,3 ГБ |
| 500 000 | 768 | 1,5 ГБ | ≈ 3 ГБ |
| 1 000 000 | 768 | 3,1 ГБ | ≈ 6,1 ГБ |
| 1 000 000 | 1536 | 6,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 отвергнут. Статус: STARTED → TRANSFERRING → SUCCESS, операция асинхронная — нужен опрос в 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 — десятки моделей в одном окне. Оплата картой РФ и по СБП.