SearXNG на сервере: частые ошибки и решения
SearXNG поднялся, страница отвечает, а через день это уже не тот SearXNG: контейнер уходит в Restarting, ИИ-агент вместо JSON получает 403, а Google с Bing один за другим показывают в логах «Suspended: too many requests». Ни одна из этих ошибок не баг — задокументированное поведение конкретных версий, и лечится за минуты, если знать слой. Разберём по точным текстам ошибок, строкам settings.yml и командам проверки.
Содержание
- Два уровня «SearXNG не работает»: контейнер и логика поиска
- Контейнер падает в restart loop: права тома и путаница между uWSGI и Granian
- `403 Forbidden` на `format=json`: ИИ-агент не получает результаты
- Движки один за другим уходят в «Suspended: too many requests»
- `secret_key: ultrasecretkey` и лимитер, который не поднимается без Valkey
- Reverse proxy: не тот IP для лимитера и битые ссылки за Nginx
- Какой сервер под SearXNG брать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Два уровня «SearXNG не работает»: контейнер и логика поиска
За жалобой «SearXNG не работает» почти всегда стоит один из двух слоёв, а симптом на экране у обоих одинаковый — пустая страница или отсутствие результатов. Слой 1 — контейнер и процесс: SearXNG не стартовал или падает через секунды после запуска, до конфига дело не доходит. Слой 2 — логика поиска: процесс жив и отвечает на /, но конкретный запрос, движок или клиент — curl, ИИ-агент, браузер за прокси — получает не то, что должен.
| Симптом | Слой | Где смотреть |
|---|---|---|
docker compose ps показывает Restarting или Exited | 1 — процесс | docker compose logs -f searxng-core |
UI открывается, но curl .../search?format=json — 403 | 2 — формат ответа | search: в settings.yml |
| Часть движков молчит, источников в выдаче меньше обычного | 2 — движки-провайдеры | /stats/errors |
| Разные пользователи видят чужие капчи и ограничения | 2 — лимитер за прокси | заголовки в конфиге Nginx |
Три команды перед тем, как лезть в конфиг:
docker compose ps
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/healthz
curl -s http://127.0.0.1:8080/stats/errors | head -40
/healthz — тот же адрес, который каждые 30 секунд дёргает встроенный в образ Docker HEALTHCHECK; код 200 значит, что процесс жив, а docker compose ps в этот момент покажет healthy. Не 200 — идите в раздел про контейнер, менять settings.yml рано. 200 есть, а результатов подозрительно мало — вопрос не в контейнере, а в /stats/errors: страница называет по каждому движку причину последнего отказа текстом самого исключения.
Контейнер падает в restart loop: права тома и путаница между uWSGI и Granian
Самая частая причина Restarting — несовпадение прав между тем, что просит образ, и тем, что разрешает ваш docker-compose.yml. Entrypoint-скрипт при старте копирует settings.yml из шаблона, если файла ещё нет в томе, и, если процесс запущен от root, рекурсивно меняет владельца тома на searxng:searxng — по умолчанию, через FORCE_OWNERSHIP=true. Для смены владельца нужна capability CHOWN.
Если вы из соображений безопасности добавили в сервис cap_drop: ALL и не вернули нужные права, entrypoint падает на этом же шаге. В логе будет что-то из этого набора — зависит от версии образа:
cp: can't create '/etc/searxng/uwsgi.ini': Permission denied
chown: /etc/searxng/uwsgi.ini: Operation not permitted
[uwsgi] unable to create the server socket: Operation not permitted
Раньше эти три строки означали попытку создать uwsgi.ini — SearXNG до недавнего времени поднимался через uWSGI. Начиная с образа 2025.7.4-01be261 контейнер перешёл на Granian: uwsgi.ini не используется и его можно удалить после проверки работоспособности. Суть проблемы с правами не изменилась — entrypoint споткнётся не на создании uwsgi.ini, а на chown тома или на попытке Granian занять сокет. Лечится одним из двух способов:
- убрать
cap_drop: ALLиз описания сервиса — так собран официальныйdocker-compose.yml; - оставить
cap_drop: ALL, но точечно вернуть:cap_add: [CHOWN, SETUID, SETGID].
Второй источник путаницы — устаревшие гайды. Репозиторий searxng-docker с Caddy, на который до сих пор ссылается большинство статей, заархивирован 28 марта 2026 года; актуальный docker-compose.yml лежит в основном репозитории, в каталоге container/, и поднимает два сервиса — searxng-core (searxng/searxng:${SEARXNG_VERSION:-latest}) и searxng-valkey (valkey/valkey:9-alpine). Контейнер redis вместо searxng-valkey в логах — признак старой схемы. И об версиях: SEARXNG_VERSION не семвер, а дата вида 2026.8.22-9fea412, так что latest в проде — плохая идея: между docker compose pull может смениться и сервер приложений, и дефолты settings.yml.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть SearXNG`403 Forbidden` на `format=json`: ИИ-агент не получает результаты
Это самая частая причина, по которой SearXNG «не работает» именно как бэкенд для ИИ — для Open WebUI, Dify, LangChain, n8n или собственного RAG-пайплайна. Веб-интерфейс на / открывается нормально, а запрос вида:
curl -s "http://127.0.0.1:8080/search?q=test&format=json"
возвращает не JSON, а:
403 Forbidden
You don't have the permission to access the requested resource.
Причина — не блокировка и не баг, а настройка по умолчанию. Формат ответа в settings.yml задаётся списком, и «из коробки» в нём только html:
search:
formats:
- html
Пока json нет в этом списке, SearXNG отклоняет такой запрос на уровне приложения — ещё до обращения к движкам. Правка простая:
search:
formats:
- html
- json
Дальше — перезапуск и повторная проверка, уже по коду ответа:
docker compose restart searxng-core
curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8080/search?q=test&format=json"
# 200
Не полагайтесь только на код ответа при интеграции с чужими или публичными инстансами: часть честно отдаёт 403, а часть — под видом 200 OK возвращает HTML-страницу проверки на бота вместо JSON, и response.json() в коде агента падает уже не с 403, а с ошибкой разбора. На своём сервере так не будет, но привычку проверять заголовок Content-Type: application/json, а не только статус, стоит оставить — надёжнее, чем ловить JSONDecodeError в рантайме агента.
Движки один за другим уходят в «Suspended: too many requests»
SearXNG не имеет официальных API-ключей ни к Google, ни к Bing — он делает то же, что обычный браузер, только с сервера и в промышленных объёмах. Рано или поздно движок отвечает не результатами, а капчей или кодом «слишком много запросов», и SearXNG сам ставит его на паузу. Длительность паузы жёстко зашита в settings.yml, в блоке suspended_times, и зависит от типа отказа:
| Исключение | Типичная причина | Пауза по умолчанию |
|---|---|---|
SearxEngineTooManyRequests | движок ответил кодом «too many requests» | 180 с (3 минуты) |
SearxEngineAccessDenied | сайт отклонил сам запрос | 180 с (3 минуты) |
SearxEngineCaptcha | движок показал капчу | 3600 с (1 час) |
cf_SearxEngineAccessDenied | блокировка именно от Cloudflare | 86400 с (1 сутки) |
recaptcha_SearxEngineCaptcha | капча размечена как reCAPTCHA | 604800 с (7 суток) |
cf_SearxEngineCaptcha | капча за подписью Cloudflare | 1296000 с (15 суток) |
Заметить это просто: в выдаче внезапно на 3-5 источников меньше, чем обычно. Понять причину — через /stats/errors, без похода в логи контейнера.
Хуже, если под паузу разом попадают именно Google и Bing — самые требовательные к репутации IP движки по умолчанию. С дата-центрового адреса, особенно у бюджетного провайдера с «засвеченной» подсетью, они блокируют почти сразу. Обычная капча снимается через час, капча reCAPTCHA — через 7 суток, а капча за подписью Cloudflare — через все 15. Перезапуск контейнера паузу не снимает: таймер живёт в памяти процесса, а не в конфиге.
Практичный выход — не бороться за Google и Bing, а исключить их из списка и опереться на движки, которые переносят серверный трафик спокойнее:
engines:
- name: google
disabled: true
- name: bing
disabled: true
DuckDuckGo, Brave, Startpage, Mojeek и Wikipedia в среднем реже требуют капчу с серверных подсетей — не потому что «разрешают», а потому что их анти-бот эвристики мягче к чистым IP. Это вопрос адреса сервера, а не только конфига — вернёмся к нему в разделе про выбор сервера.
`secret_key: ultrasecretkey` и лимитер, который не поднимается без Valkey
Свежий settings.yml из шаблона содержит secret_key: "ultrasecretkey" — буквально этот текст, одинаковый на каждой инсталляции в мире. Если файл создаётся автоматически при первом запуске контейнера (тома ещё не было), entrypoint сам подставляет вместо заглушки случайное значение из /dev/urandom. Но если вы принесли settings.yml со стороны — из старого гайда, архивного searxng-docker или через docker cp — заглушка так и остаётся заглушкой. Проверка:
docker compose exec searxng-core grep secret_key /etc/searxng/settings.yml
Ручная замена — тем же способом, что в документации для установки без Docker:
sed -i -e "s/ultrasecretkey/$(openssl rand -hex 32)/g" /etc/searxng/settings.yml
Менее очевидная ловушка — рейт-лимит и бот-детект (server.limiter: true) требуют поднятого Valkey, форка Redis, и без него ведут себя не так, как кажется. По умолчанию в settings.yml стоит valkey: url: false. Включили limiter: true, не указав рабочий url — поведение зависит ещё от одной настройки, public_instance:
public_instance: true— процесс явно завершается (sys.exit(1)), и вы получаете тот же restart loop, что и в разделе про права, только с другой причиной;public_instance: false(обычный случай для частного сервера) — SearXNG не падает и не пишет заметной ошибки на старте, а просто тихо не включает лимитер. Единственный след — строка в логе уровня error:The limiter requires Valkey, please consult the documentation.
То есть на закрытом инстансе бот-защита может быть выключена месяцами, а вы об этом не узнаете, пока не начнёте искать специально. Рабочий конфиг для связки с сервисом searxng-valkey:
server:
limiter: true
valkey:
url: valkey://searxng-valkey:6379/0
Reverse proxy: не тот IP для лимитера и битые ссылки за Nginx
Если SearXNG стоит за Nginx на своём домене — а для внешнего доступа иначе не имеет смысла — есть три места, где прокси незаметно ломает логику, которая у «голого» контейнера работала штатно.
Первое — заголовки X-Forwarded-For и X-Real-IP. Лимитер и бот-детект решают по IP клиента, и если Nginx их не прокидывает, все запросы приходят с одним адресом — адресом самого Nginx. Итог обратный ожидаемому: не «лимитер блокирует бота», а «один пользователь исчерпал лимит, и капчу теперь видят все», включая вас. Минимальный рабочий блок:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
Второе — server.base_url. Не совпадает с реальным внешним адресом или оставлен как false — внутренние ссылки (пагинация, переход по результату, картинки через прокси-режим) собираются неправильно, и часть UI за доменом не открывается, хотя первая страница выглядит рабочей.
Третье — смешение старого и нового шаблона. В документации рядом лежат два примера: сокетный, для uWSGI (uwsgi_pass unix:///usr/local/searxng/run/socket; с параметрами uwsgi_param), и HTTP-based для контейнера — как в блоке выше. uwsgi_param работает только внутри location с uwsgi_pass; вставили её в блок с proxy_pass — nginx -t откажется стартовать с nginx: [emerg] unknown directive "uwsgi_param". И не пугайтесь строки X-Forwarded-For header is not set! X-Real-IP header is not set! в логе SearXNG: так бот-детект жалуется на встроенный HEALTHCHECK, который стучится в /healthz напрямую, минуя Nginx. Это шум, а не ошибка конфигурации.
Какой сервер под SearXNG брать в MAATRIX
SearXNG сам по себе — тонкий процесс на Flask поверх Granian: не считает эмбеддинги и не хранит индекс, а параллельно опрашивает внешние движки и разбирает их ответы. Нагрузка не процессорная, а сетевая и по числу соединений: чем активнее вы или ИИ-агент шлёте запросы, тем больше держится параллельных исходящих подключений разом и тем заметнее память на воркер. Разработчики прямо предупреждают не трогать число воркеров Granian вручную — по их словам, это увеличивает расход ресурсов и мешает бот-детекту.
Честный минимум: 1 vCPU, 1-2 ГБ RAM, 20 ГБ NVMe. Такой сервер тянет SearXNG без Valkey — то есть без лимитера, как в разделе выше. Для частного инстанса на одного-двух пользователей или одного ИИ-агента это осознанный компромисс, а не поломка. Поднимаете Valkey, включаете limiter: true, растёт число клиентов — память становится тесной быстрее процессора; точный расчёт под ваш профиль запросов — в статье «Сколько RAM нужно для SearXNG и Perplexica».
Комфортный вариант: 2 vCPU, 4 ГБ RAM, 40 ГБ NVMe. Хватает на SearXNG вместе с searxng-valkey, включённый лимитер, десяток активных движков и разборы /stats/errors — без запаса памяти под них разделы 1 и 4 этой статьи пришлось бы проходить вслепую.
Локация — Великобритания (Лондон). SearXNG — приватная альтернатива поисковикам с трекингом, и держать его под юрисдикцией с понятным режимом персональных данных — продолжение этой идеи, а не формальность. Плюс низкий пинг до остальной Европы, если ИИ-инструмент, который дёргает SearXNG — тот же Perplexica или свой агент на LangChain, — тоже развёрнут в ЕС. На капчи из раздела про движки локация напрямую не влияет: Google одинаково подозрителен к дата-центровым адресам что в Лондоне, что в Нью-Йорке. Но чистый выделенный IP, не засвеченный чужим трафиком, снижает частоту капчи заметнее, чем страна.
При заказе сервера SearXNG из каталога apps.maatrix.io ставится автоматически — на Ubuntu и на Debian, без ручных команд из разделов про контейнер выше: адрес панели и ключи доступа появятся в личном кабинете, в разделе «Доступ». А вот format: json под ИИ-агента, список отключённых движков и связка limiter + valkey — решения по вашей задаче, автоустановка их не примет; разбор из этой статьи остаётся актуальным и для сервера, поднятого через каталог.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть SearXNGОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Веб-интерфейс работает, а curl .../search?format=json — всё равно 403. Что не так?
Проверьте, что правили именно тот settings.yml, который смонтирован в контейнер (docker compose exec searxng-core cat /etc/searxng/settings.yml), и что контейнер перезапущен: docker compose restart searxng-core. Исключите обратный прокси — если 403 отдаёт Nginx, а не SearXNG, в теле ответа не будет фразы You don't have the permission..., характерной именно для приложения.
После обновления образа контейнер перестал подниматься, хотя настройки не трогали. В чём может быть дело?
Начиная с образа 2025.7.4-01be261 SearXNG перешёл с uWSGI на Granian; правки в uwsgi.ini при обновлении просто игнорируются, серверные параметры нужно перенести в переменные GRANIAN_*. Заодно проверьте cap_drop: ALL — с новым entrypoint это частая причина падения на chown тома.
Нужен ли Valkey, если SearXNG стоит только для меня одного?
Нет: без него инстанс с public_instance: false работает штатно, просто без рейт-лимита и бот-детекта. Valkey обязателен, как только вы даёте доступ ещё кому-то — коллеге, второму ИИ-агенту — или ставите public_instance: true: тогда без Valkey процесс не запустится вовсе.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.