Typesense на сервере: частые ошибки и решения
Typesense рекламируют как поисковый движок, который поднимается за пять минут и не требует настройки маппингов, как Elasticsearch. Отчасти это правда — но именно из-за этой простоты новички чаще спотыкаются на деталях: правах на каталог с данными, схеме API-ключей или банальной нехватке памяти. Ниже — конкретные ошибки, с которыми реально сталкиваются на VPS и выделенных серверах, и рабочие способы их закрыть.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Установка: systemd или Docker, и почему это не мелочь
Typesense можно поставить двумя способами, и от выбора зависит половина будущих проблем с диагностикой.
Через systemd (бинарник + конфиг):
curl -O https://dl.typesense.org/releases/TYPESENSE_VERSION/typesense-server-TYPESENSE_VERSION-amd64.deb
sudo dpkg -i typesense-server-*.deb
Точную актуальную версию берите со страницы релизов на typesense.org — я намеренно не привожу здесь конкретный номер, чтобы не сослаться на устаревший тег. Пакет ставит бинарник в /opt/typesense, конфиг — в /etc/typesense/typesense-server.ini, данные — в /var/lib/typesense.
Через Docker Compose:
services:
typesense:
image: typesense/typesense:27.1
restart: unless-stopped
ports:
- "127.0.0.1:8108:8108"
volumes:
- ./data:/data
command: '--data-dir /data --api-key=${TYPESENSE_API_KEY} --enable-cors'
Пин конкретной версии образа (27.1, а не latest) — не паранойя, а необходимость: Typesense между минорными релизами иногда меняет формат хранения на диске, и после docker compose pull без фиксированного тега сервис может отказаться стартовать на старых данных.
Порт 8108 в примере привязан к 127.0.0.1 — наружу его выпускать не стоит, доступ снаружи должен идти через reverse-proxy (см. ниже) или в крайнем случае через SSH-туннель для отладки.
"Failed to initialize DB" при первом запуске
Самая частая ошибка новичков — Typesense не может открыть RocksDB в каталоге данных:
Failed to initialize DB: IO error: While lock file: /var/lib/typesense/db/LOCK: Permission denied
Причины обычно две:
- Права на каталог. Пакетная установка создаёт пользователя
typesense, но если каталог данных вы указали вручную (например, отдельный диск, смонтированный в/mnt/data), владельцем остаётсяroot:
sudo chown -R typesense:typesense /mnt/data/typesense
sudo systemctl restart typesense-server
В Docker то же самое, только владелец процесса внутри контейнера — обычно UID 1000, а не тот, что был у каталога на хосте до монтирования volume.
- Каталог уже занят другим процессом. RocksDB блокирует директорию эксклюзивно — если у вас случайно запущены два экземпляра Typesense на одних данных (например, старый systemd-юнит не остановился, а вы параллельно подняли Docker-контейнер с тем же
--data-dir), второй процесс не стартует. Проверьте:
sudo lsof +D /var/lib/typesense/db
ps aux | grep typesense-server
Если данные повреждены после аварийного выключения хоста, а не просто заблокированы, самый надёжный путь — восстановиться из снапшота (см. ниже), не пытаясь чинить RocksDB руками.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать сервер401 Unauthorized: путаница между admin-ключом и search-only
Вторая по частоте ошибка — не про установку, а про доступ к уже работающему серверу:
{"message": "Forbidden - a valid `x-typesense-api-key` header must be sent."}
Typesense различает два типа ключей, и это критично для безопасности продакшена:
- Master/admin key — тот, что вы задали флагом
--api-keyпри старте. Даёт полный доступ: создание коллекций, удаление данных, управление ключами. Использовать его на фронтенде — прямая дыра: любой пользователь сайта сможет открыть devtools и удалить вашу коллекцию. - Search-only key — генерируется через API специально для клиентской стороны, ограничен конкретными коллекциями и только на чтение.
Создать ограниченный ключ:
curl -X POST 'http://localhost:8108/keys' \
-H "X-TYPESENSE-API-KEY: ${ADMIN_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"description": "Search-only key for website",
"actions": ["documents:search"],
"collections": ["products"]
}'
Полученный ключ (в ответе будет поле value) и идёт в браузерный код — InstantSearch, Vue или React-виджет. Admin-ключ хранится только на сервере, в переменных окружения бэкенда, и никогда не попадает в клиентский бандл.
Отдельная засада: заголовок регистронезависим (X-TYPESENSE-API-KEY или x-typesense-api-key — без разницы), но если вы проксируете запросы через Nginx, убедитесь, что он не режет заголовки с подчёркиванием или нестандартным регистром — по умолчанию Nginx это пропускает, но некоторые конфиги с underscores_in_headers off могут вмешаться, если вы используете кастомные заголовки рядом.
Контейнер падает от нехватки памяти
Typesense — in-memory движок: весь индекс (не только "горячие" данные, а целиком) держится в RAM для скорости поиска в миллисекунды. Это осознанный компромисс движка, а не баг, но именно он приводит к внезапным падениям:
Killed
в логах systemd или Docker обычно означает, что OOM killer ядра пристрелил процесс, когда индекс перестал помещаться в доступную память. Проверить, что это именно OOM:
dmesg -T | grep -i "out of memory"
journalctl -u typesense-server --since "10 min ago"
Что делать:
- Оценивайте память заранее. Точных цифр "байт индекса на документ" я специально не привожу — это сильно зависит от количества полей, длины текста и того, сколько полей вы пометили как
facet/sort(они хранятся в дополнительных структурах). Ориентир на практике: закладывайте RAM с запасом в 2-3 раза больше, чем "на глаз" кажется нужным по размеру исходных данных в JSON, и проверяйте фактическое потребление через/metrics.jsonуже на реальном датасете. - Мониторьте эндпоинт метрик.
curl http://localhost:8108/metrics.json -H "X-TYPESENSE-API-KEY: ${ADMIN_KEY}"
В ответе — memory_active_bytes, memory_allocated_bytes, disk_used_bytes. Настройте алерт заранее, а не после первого падения.
- Не индексируйте лишние поля. Если поле не участвует в поиске, фильтрации или сортировке, помечайте его
"index": falseв схеме коллекции — оно всё равно вернётся в результатах, но не займёт место в индексных структурах. - При нехватке памяти на VPS проще увеличить план, чем городить шардирование вручную — Typesense начинает поддерживать кластеризацию с распределением нагрузки, но для одного узла это оверинжиниринг, если задача — просто дать серверу больше RAM.
CORS-ошибки при обращении из браузера
Если фронтенд с одного домена стучится в Typesense на другом (или на другом порту), а сервер поднят без --enable-cors, в консоли браузера будет классическое:
Access to fetch at 'https://search.example.com:8108/...' from origin 'https://example.com'
has been blocked by CORS policy
Решение — флаг при старте:
typesense-server --data-dir=/var/lib/typesense --api-key=${API_KEY} --enable-cors
или в Docker Compose добавить --enable-cors в command. По умолчанию флаг разрешает CORS для всех источников — если это неприемлемо (внутренний сервис с чувствительными данными), ограничьте через --cors-domains:
--enable-cors --cors-domains=https://example.com,https://www.example.com
Важный нюанс: --enable-cors без --cors-domains можно использовать только вместе с search-only ключом на фронтенде — открытый CORS плюс admin-ключ в браузерном коде это прямая уязвимость независимо от происхождения запросов.
Nginx как reverse-proxy перед Typesense: SSL и таймауты
В продакшене Typesense почти всегда прячут за Nginx с SSL, а не выставляют порт 8108 напрямую (кроме прочего, порт без TLS — это API-ключ, летающий в открытом виде). Базовый конфиг:
server {
listen 443 ssl http2;
server_name search.example.com;
ssl_certificate /etc/letsencrypt/live/search.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/search.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8108;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
# запросы на импорт больших датасетов идут долго
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# массовый импорт документов может быть увесистым
client_max_body_size 100m;
}
}
Выпустить сертификат можно через Let's Encrypt стандартным способом, либо через Caddy, если хочется автопродления без отдельных cron-задач.
Два нюанса, которые ловят на проде:
- Таймауты при bulk-импорте. Если вы заливаете коллекцию из миллиона документов одним запросом к
/collections/{name}/documents/import, дефолтныйproxy_read_timeoutв 60 секунд у Nginx оборвёт соединение раньше, чем Typesense успеет ответить, хотя сам импорт на стороне сервера продолжится. Решение — либо увеличить таймаут, как в примере выше, либо (правильнее) слать документы пачками по несколько тысяч штук, а не всё разом. client_max_body_size. Забытый дефолт в 1 МБ у Nginx обрежет запрос на импорт с ошибкой 413 ещё до того, как он дойдёт до Typesense — в логах приложения при этом будет просто "connection reset", что сбивает с толку при диагностике.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Нужен ли Typesense отдельный сервер или хватит одного VPS с остальными сервисами?
Для небольших и средних проектов (до нескольких миллионов документов) достаточно совместного размещения с бэкендом — главное честно посчитать память под индекс и не сажать Typesense на одну машину с чем-то ещё таким же прожорливым, как Elasticsearch или база данных с большим кэшем.
Как сделать бэкап данных Typesense?
Штатный механизм — снапшоты через API: curl -X POST "http://localhost:8108/operations/snapshot?snapshot_path=/backup/ts-snapshot" -H "X-TYPESENSE-API-KEY: ${ADMIN_KEY}". Полученный каталог снапшота можно копировать в другое хранилище обычным rsync или через restic — учтите, что операция снапшота требует свободного места на диске, равного текущему размеру данных.
Почему после рестарта сервер долго не отвечает на запросы?
При запуске Typesense загружает весь индекс с диска в память заново — на больших коллекциях (десятки миллионов документов) это может занимать заметное время, в течение которого API отвечает 503 с сообщением о том, что сервер ещё не готов. Это нормальное поведение, а не зависание — просто подождите, ориентируясь на размер данных.
Чем Typesense принципиально отличается от Meilisearch и стоит ли выбирать между ними по ошибкам установки?
Оба движка близки по духу (простая установка, типизированные схемы, опечатко-устойчивый поиск), но у Typesense изначально сильнее поддержка фасетов, геопоиска и кластеризации. Если вы уже сравниваете варианты, у нас есть отдельный разбор по установке Meilisearch на VPS — конфигурация окружения там очень похожа.
Можно ли использовать Typesense Cloud вместо самостоятельного хостинга?
Можно, но для проектов с российской юрисдикцией это часто добавляет сложностей с оплатой и латентностью до ближайшего региона — самостоятельный VPS с оплатой картой или криптой из России снимает оба вопроса разом.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →