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

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

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

MAATRIX

Контейнер поднимается за минуту, а дальше начинается настоящая работа: инстанс молча падает на дефолтном secret_key, ссылки в выдаче ведут на http://localhost:8080, а Perplexica вместо JSON получает 403 Forbidden. Установка SearXNG на VPS — это не docker compose up, а десяток решений в settings.yml, от которых зависит, будет поиск работать или молчать. Ниже — весь путь: обязательные параметры, разбор конфига по секциям, движки, лимитер с Valkey, обратный прокси и подключение ИИ-приложений.

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

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

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

Что такое SearXNG и когда он нужен на VPS

SearXNG — метапоисковик: собственного индекса и краулера у него нет. Он принимает запрос, рассылает его параллельно во внешние источники — Google, DuckDuckGo, Brave, Wikipedia, Stack Overflow, GitHub, arXiv и ещё сотню движков из поставки, — разбирает ответы и склеивает их в одну выдачу без рекламы и профилирования. Формально это форк searx, но кодовые базы разошлись давно: старые конфиги searx переносить бессмысленно, половина ключей просто не существует.

Из архитектуры следуют три вещи. Наружу ходит ваш сервер, а не браузер пользователя — отсюда приватность, но и главная боль: датацентровые IP поисковики знают и охотно показывают им капчу вместо результатов. Диск почти не нужен — хранить нечего, ни индекса, ни кэша страниц. Нагрузка — сеть и парсинг HTML: один запрос превращается в десяток параллельных HTTPS-соединений, процессор нужен короткими всплесками, а не постоянно.

На VPS SearXNG чаще всего ставят как поисковый бэкенд для ИИ-приложений: Perplexica, Open WebUI, n8n и самописные RAG-пайплайны ходят к нему за JSON, потому что это единственный способ дать модели свежий веб без платного Serper или SerpAPI. Второй сценарий — личный поисковик для себя и коллег, третий — публичный инстанс из списка searx.space.

Сценарий стоит выбрать до установки: приватный инстанс живёт с limiter: false и без Valkey, публичный обязан включить оба — иначе открытый шлюз бесплатно скрейпит Google для чужих ботов, о чём подробнее в пятой секции.

Обязательный минимум: secret_key, base_url и use_default_settings

Два параметра ломают инстанс молча, если о них забыть.

server.secret_key. В поставке — заглушка, и с ней приложение либо не стартует, либо путает cookie между пользователями. В логе контейнера это выглядит так:

ERROR:searx.webapp: server.secret_key is not changed. Please use something else than ultrasecretkey.

После этого docker compose ps показывает Exited (1). Генерируется один раз командой openssl rand -hex 32 и больше не меняется: ключ подписывает cookie с настройками и токены лимитера, смена сбрасывает их у всех разом. В официальном образе есть переменная SEARXNG_SECRET — скрипт запуска сам подставит значение в settings.yml вместо заглушки, и хранить ключ в YAML, который лежит в git, не придётся.

server.base_url. Полный внешний адрес со схемой и слэшем в конце: https://search.example.com/. По нему строятся ссылки в OpenSearch (кнопка «добавить поиск в браузер»), в RSS и в редиректах после сохранения настроек. Симптом неверного значения: интерфейс открывается по домену, а после «Сохранить» в настройках браузер уезжает на http://localhost:8080/.

Третье — без строки use_default_settings: true ваш settings.yml считается полным набором, и SearXNG стартует вообще без движков: выдача пустая, а в логах ни одной ошибки. Минимальный рабочий файл:

use_default_settings: true
server:
  secret_key: "ultrasecretkey"   # подставится из SEARXNG_SECRET
  base_url: "https://search.example.com/"
  limiter: false
  image_proxy: true
  method: "GET"

Живость проверяется отдельным эндпоинтом без авторизации:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/healthz
200

Порт контейнера стоит публиковать только на 127.0.0.1:8080:8080 — наружу пускает Nginx из шестой секции, открытый 8080 без TLS не нужен.

Развернуть за пару минут

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

Развернуть SearXNG

settings.yml по секциям: search, server, outgoing

Дефолт рассчитан на анонимный публичный инстанс, а вы настраиваете свой. Разберём параметры, которые реально меняют поведение.

search. default_lang: "ru" — без него русскоязычные запросы регулярно получают англоязычную выдачу. autocomplete: "duckduckgo" включает подсказки; по умолчанию источник не выбран, и подсказки просто молчат. Пара ban_time_on_fail / max_ban_time_on_fail — секунды, на которые упавший движок выпадает из опроса, с удвоением при повторах.

Главная строка — formats. По умолчанию там только html. Пока не добавите json, любой запрос вида /search?q=test&format=json вернёт 403 Forbidden — именно это ловят все, кто подключает Perplexica или Open WebUI:

search:
  default_lang: "ru"
  formats: [html, json]

server. method: "GET" — по умолчанию форма отправляется POST, и ссылку на результаты нельзя скопировать или дёрнуть из скрипта; для интеграций и шарящихся URL ставят GET. image_proxy: true заворачивает картинки через ваш сервер — хосты изображений не видят IP пользователя, честная плата — трафик и процессор на каждую миниатюру.

outgoing. request_timeout: 3.0 — три секунды на ответ движка по умолчанию. На канале с большими задержками до Европы часть источников не успевает, и выдача выглядит бедной без единой ошибки в логе; поднимать до 5–6 секунд можно, но общий ответ пользователю ждёт самый медленный движок.

Движки, категории и bang-синтаксис

Движок — это описание того, как сформировать запрос к источнику и разобрать ответ. В поставке их больше сотни, включена по умолчанию примерно треть.

Правило слияния неинтуитивное: при use_default_settings: true движки в вашем файле дополняют дефолтные по совпадению имени, а не заменяют список:

engines:
  - name: google
    timeout: 4.0
    weight: 2
  - name: wikidata
    disabled: true

Для короткого белого списка есть отдельный синтаксис:

use_default_settings:
  engines:
    keep_only: [google, duckduckgo, brave, wikipedia, github]

Оставлять пять движков вместо сорока разумно: поиск быстрее, а сорок параллельных запросов с одного адреса — верный способ познакомиться с чужой защитой от ботов. weight управляет ранжированием, а shortcut включает bang-синтаксис, ради которого SearXNG часто и ставят: !go пример ищет только в Google, !wp — только в Википедии, :ru новости задаёт язык результатов, а !!go пример с двумя знаками делает редирект прямо в выдачу движка, минуя SearXNG.

Проверять после правок — страница /preferences со временем ответа каждого движка и /stats/errors с недавними отказами. Честное ограничение: часть движков со временем ломается — источники меняют вёрстку, требуют JavaScript или блокируют датацентровые подсети, Google и Bing особенно охотно отвечают серверным адресам капчей. Это условия игры метапоиска, а не поломка вашей установки; разбор пустой выдачи — в статье SearXNG выдаёт пустую выдачу: причины и решение.

Valkey и лимитер: защита от ботов и от своих же приложений

Лимитер — встроенная защита от автоматических запросов: считает обращения с адреса, проверяет заголовки браузера и выдаёт ссылкам скрытый токен, который настоящий браузер подхватывает, а скрипт — нет. Работает он только с внешним хранилищем. Исторически это был Redis; после смены его лицензии SearXNG перешёл на Valkey — в старых руководствах встретите redis: и SEARXNG_REDIS_URL, читайте как valkey: и SEARXNG_VALKEY_URL.

valkey:
  url: valkey://searxng-valkey:6379/0
server:
  limiter: true
  public_instance: false

Если включить limiter: true, а хранилище не поднять, защита молча не работает — инстанс продолжит отвечать всем подряд. Проверяют не по конфигу, а по факту:

docker exec -it searxng-valkey valkey-cli ping
PONG

Тонкая настройка — в отдельном файле /etc/searxng/limiter.toml. Ключевые секции:

[real_ip]
x_for = 1

[botdetection.ip_limit]
link_token = true

[botdetection.ip_lists]
pass_ip = ["172.16.0.0/12"]

x_for = 1 значит «доверять последнему значению в X-Forwarded-For» — одному прокси перед приложением; при неверном значении SearXNG либо забанит всех разом при первом всплеске, либо не забанит никого. link_token = true — самая жёсткая проверка, она отсекает ботов и вместе с ними ваши же приложения. Отсюда правило для смешанного сценария: адреса своих сервисов — в pass_ip, и если Perplexica живёт в той же docker-сети, разрешать нужно её подсеть, а не 127.0.0.1:

docker network inspect searxng_searxng --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'

Честно: лимитер защищает от неаккуратных ботов, а не от целенаправленной нагрузки. На публичном инстансе за ним всё равно нужны ограничения на уровне Nginx.

Обратный прокси, TLS и подключение ИИ-приложений

Наружу SearXNG выставляют только через прокси с TLS:

server {
    listen 443 ssl;
    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:8080;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
    }
}

Три заголовка обязательны: без X-Forwarded-For лимитер не увидит клиента, без X-Forwarded-Proto приложение решит, что соединение не зашифровано, и будет строить ссылки на http://, без Host сломается сопоставление с base_url. Сертификат — certbot --nginx -d search.example.com, фаервол закрывает лишнее:

ufw allow 22/tcp
ufw allow 80,443/tcp
ufw deny 8080/tcp

Для приватного инстанса добавьте auth_basic. Оговорка: Basic Auth ломает интеграции, которые не шлют заголовок Authorization, поэтому приложения соединяют с SearXNG не через домен, а по внутреннему адресу, минуя Nginx. Perplexica — в config.toml:

[API_ENDPOINTS]
SEARXNG = "http://searxng:8080"

Open WebUI — переменными: ENABLE_RAG_WEB_SEARCH=true, RAG_WEB_SEARCH_ENGINE=searxng, SEARXNG_QUERY_URL=http://searxng:8080/search?q=<query>. В обоих случаях searxng — имя сервиса в docker-compose, работающее как DNS внутри общей сети; 127.0.0.1 внутри контейнера — это сам контейнер. Сборка связки целиком — в статье про установку Perplexica на VPS.

Проверка одной командой — ответ должен быть JSON, а не HTML:

curl -s 'http://127.0.0.1:8080/search?q=nginx+timeout&format=json' \
  | jq '{results: (.results|length), engines: .unresponsive_engines}'

unresponsive_engines полезнее всего — там движки, не ответившие на этот запрос, с причиной. number_of_results часто равен нулю даже при полной выдаче: метапоиск не знает общего числа совпадений и не пытается его выдумать. Пришла HTML-страница с 403 вместо JSON — не добавлен json в search.formats либо сработал лимитер; каталог типовых поломок — в статье про частые ошибки SearXNG на сервере.

Какой сервер под SearXNG брать в MAATRIX

Требования у SearXNG скромные: индекса нет, база не нужна, диск занимает код и логи. Но два ресурса недооценивают.

Честный минимум: 1 vCPU, 2 ГБ RAM, 20 ГБ NVMe. Хватает для личного инстанса на одного-двух человек и пары приложений рядом. Ограничение прямое: один воркер обрабатывает запрос целиком, пока идёт поиск — второй пользователь ждёт; лечится ростом UWSGI_WORKERS, но каждый воркер — отдельный процесс Python, память растёт линейно с их числом. На 1 ГБ ставить не стоит: рядом обычно просятся Valkey, Nginx и контейнер ИИ-приложения, а всплеск при парсинге десятка HTML-ответов — верный визит OOM-киллера.

Комфортный вариант: 2 vCPU, 4 ГБ RAM, 40 ГБ NVMe. Помещаются SearXNG с четырьмя воркерами, Valkey, Nginx с TLS и запас на логи — достаточно для команды и роли поискового бэкенда у нескольких сервисов. Если рядом ещё и локальная модель для Perplexica, считать нужно по ней, а не по поиску — расчёт в статье сколько RAM нужно для SearXNG и Perplexica.

Взять с запасом стоит канал: при image_proxy: true каждая миниатюра в выдаче идёт через ваш сервер, и на активном инстансе картинки дают больше трафика, чем весь остальной поиск.

Локация — Лондон (UK). Для метапоиска локация важнее конфигурации: запросы к движкам уходят с адреса сервера, и поисковики оценивают именно его. Британский IP европейского провайдера получает нормальную выдачу там, где адрес из подсети, замеченной в скрейпинге, — капчу. До дата-центров Google, DuckDuckGo и Brave в Европе из Лондона десятки миллисекунд, и трёхсекундный request_timeout перестаёт быть проблемой; рядом — соседство с GDPR, если инстансом пользуются не только вы. США берут, если рядом на том же сервере живут ИИ-сервисы с американским IP, Франция — альтернатива Лондону с тем же профилем. Россия для SearXNG подходит хуже всего: часть движков с российских адресов отвечает через раз.

Разворачивать вручную не нужно: SearXNG есть в каталоге apps.maatrix.io и ставится автоматически при заказе сервера — на Ubuntu и на Debian. Адрес интерфейса и доступы появляются в личном кабинете, в разделе «Доступ»; остаётся зайти, поправить settings.yml под свой сценарий и подключить приложения. Оплата — картой российского банка, по СБП, криптовалютой или токеном MAAT.

Развернуть за пару минут

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

Развернуть SearXNG

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

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

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

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

Perplexica и Open WebUI получают 403 вместо результатов. Что не так?

Две причины, обе в конфиге: в search.formats не добавлен json (по умолчанию только html), либо включён лимитер, а адрес приложения не внесён в pass_ip в limiter.toml — при обращении из соседнего контейнера нужна подсеть docker-сети, а не 127.0.0.1. Проверка — curl -s 'http://127.0.0.1:8080/search?q=test&format=json' | head -c 200.

Можно ли обойтись без Valkey?

Да, если инстанс приватный и закрыт фаерволом или Basic Auth — тогда server.limiter: false. Для публичного — нет: без Valkey лимитер не работает, и сервер бесплатно скрейпит Google для чужих ботов, пока движки не начнут отвечать капчей на всё подряд.

Что будет, если сменить secret_key на работающем инстансе?

Не критично, но заметно: ключ подписывает cookie с настройками и токены лимитера, поэтому у всех сбросятся выбранные движки, язык и тема. Меняйте его только при подозрении на утечку и предупредите пользователей.

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

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