Як розгорнути приватний SearXNG на власному VPS
Покроково розгорніть SearXNG на VPS через Docker Compose: налаштуйте settings.yml і limiter, додайте nginx з TLS та JSON API для власних скриптів.
Що ви створюєте
Self-hosting SearXNG дає змогу розгорнути приватну пошукову систему на власному сервері. SearXNG — це метапошукова система: вона приймає ваш запит, надсилає його до інших систем, таких як Google, Bing, DuckDuckGo і Wikipedia, а потім об’єднує отримані результати на одній сторінці. Профіль не створюється, а tracking cookie не встановлюється, оскільки єдина машина, на якій зберігається ваш запит, належить вам. Якщо ви знайшли старіші посібники для проєкту, який називався просто Searx, саме від нього було відгалужено цей проєкт. Він не підтримується з September 2023, коли в його власному репозиторії було оголошено, що Searx більше не підтримується. Єдиний наступний коміт, примітка до документації в May 2026, цього не змінив. Тому перевірте стан обох проєктів, перш ніж обрати один із них.
Стек невеликий. Два контейнери, один файл налаштувань і один reverse proxy. Він без проблем працюватиме на невеликому VPS. Це стосується не кожного self-hosted сервісу: бібліотеки фотографій, розглянуті в порівнянні PhotoPrism та Immich, визначають мінімальний обсяг RAM індексатором, а не web app. Насправді потрібно вирішити, чи буде інстанс приватним, тобто доступним лише вам і вашим скриптам, чи публічним, тобто доступним для запитів будь-кому в інтернеті. Від цього вибору залежать налаштування безпеки, тому визначте його до введення будь-яких команд. Типовий варіант — приватний інстанс.
Є й друга причина запустити власний інстанс. Інстанс SearXNG працює з JSON, тому будь-який написаний вами скрипт або AI agent отримує власний пошуковий API без key, оплати за окремі запити та quota mail.
Встановлення SearXNG за допомогою Docker Compose
Проєкт публікує образ контейнера та файл Compose. Завантажте їх на новий сервер Ubuntu 24.04, на якому вже встановлено Docker Engine і плагін Compose. Якщо Docker для вас новий, почніть із матеріалу Основи Docker Compose на VPS, а потім поверніться сюди.
sudo install -d -o "$USER" -g "$USER" -m 750 /opt/searxng
cd /opt/searxng
mkdir -p core-config
curl -fsSL \
-O https://raw.githubusercontent.com/searxng/searxng/master/container/docker-compose.yml \
-O https://raw.githubusercontent.com/searxng/searxng/master/container/.env.example
cp -i .env.example .envФайл Compose визначає два сервіси. core — це сам SearXNG, а valkey — сховище даних у пам’яті, яке використовується для обмеження частоти запитів і короткочасного стану. Воно монтує ./core-config/ у /etc/searxng/ всередині контейнера, тому всі налаштування зберігаються в одному каталозі на хості.
Тепер відредагуйте .env. Кожен рядок у прикладі, що постачається, закоментований. Саме тому контейнер запускається на порту 8080 на всіх адресах. Розкоментуйте та задайте ці три параметри.
SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080SEARXNG_HOST=127.0.0.1 — найважливіший параметр. Він задає опублікований порт як 127.0.0.1:8080:8080 замість простого 8080:8080. Docker публікує простий варіант на всіх адресах, тому контейнер відповідає лише на loopback-адресі, а інтернет не може звернутися до нього безпосередньо. Якщо пропустити цей параметр, контейнер стане доступним одразу після запуску, оскільки опублікований порт Docker розміщується перед правилами вашого firewall. Докладніше про цю пастку: опубліковані порти Docker обходять ufw.
SEARXNG_VERSION=latest підходить на етапі навчання. На важливому для вас сервері зафіксуйте tag. Станом на October 2026 теги релізів ґрунтуються на датах і мають вигляд 2026.10.4-d48c4b555, тому deployment із зафіксованою версією оновлюється тоді, коли ви вирішите, а не тоді, коли registry змінить вміст. Такий самий підхід корисний для будь-яких інших довготривалих сервісів на сервері. Саме тому в матеріалі self-hosted relay RustDesk також зафіксовано теги образів: автоматичне оновлення сервісу віддаленого доступу проявить себе в найгірший момент.
settings.yml: важливі частини
Створіть core-config/settings.yml до першого запуску. use_default_settings: true вказує SearXNG завантажити власні стандартні значення, що постачаються з пакетом, а потім застосувати лише вказані вами ключі. Тому файл залишається коротким і не втрачає сумісність під час оновлень, які додають нові параметри.
Спочатку згенеруйте секрет, оскільки це значення буде безпосередньо записано у файл.
openssl rand -hex 32use_default_settings: true
general:
instance_name: "search.example.com"
server:
base_url: "https://search.example.com/"
secret_key: "paste-the-openssl-output-here"
limiter: false
public_instance: false
image_proxy: true
valkey:
url: valkey://valkey:6379/0
search:
safe_search: 0
autocomplete: "duckduckgo"
formats:
- html
- jsonsecret_key підписує дані сеансів і токенів. Стандартним значенням є буквальний рядок ultrasecretkey. Якщо залишити його, будь-хто, хто знає це стандартне значення, зможе підробити такі токени. Замініть його один раз і більше не змінюйте: подальша зміна видалить усі збережені налаштування.
base_url має містити публічну HTTPS-адресу з кінцевою косою рискою. SearXNG вставляє цю адресу в згенеровані посилання. Якщо залишити localhost, посилання на «наступну сторінку» у віддаленому браузері вказуватиме на комп’ютер самого користувача й не працюватиме.
formats визначає типи результатів, які повертатиме веб-ендпойнт. json відсутній у стандартному списку, тому запит JSON повертає 403, доки ви не додасте цей тип. image_proxy: true передає мініатюри результатів через ваш сервер, тому сайти, на яких зберігаються ці зображення, не бачать адрес ваших відвідувачів.
valkey.url використовує ім’я хоста valkey, оскільки це ім’я сервісу у файлі Compose. Compose розміщує обидва контейнери в одній мережі, де імена сервісів розпізнаються. Якщо вказати localhost, обмеження швидкості не працюватиме, оскільки всередині контейнера core localhost означає цей самий контейнер.
Секрет зберігається у звичайному файлі, тому захистіть також каталог, у якому він розташований. chmod 750 /opt/searxng не дає іншим користувачам хоста отримати доступ до нього. Можна також змінити core-config/settings.yml на режим 600 без наслідків: точка входу офіційного контейнера запускається від імені root і, якщо FORCE_OWNERSHIP залишено зі стандартним значенням true, перед запуском SearXNG передає змонтований каталог конфігурації користувачу контейнера searxng. Тому файл залишається доступним для читання цьому користувачу. Побічний ефект буде помітний на хості: цей самий крок змінює власника core-config/ на ідентифікатор користувача контейнера, тому після цього файл потрібно буде редагувати за допомогою sudo.
Запустіть стек і перевірте його.
cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/docker compose ps має показати обидва контейнери у стані running. curl має відповідати на HTTP/1.1 200 OK. Якщо відповіді немає, перегляньте docker compose logs core, оскільки помилка YAML у settings.yml відображається там як помилка синтаксичного розбору із зазначенням номера рядка.
Налаштуйте nginx як reverse proxy з TLS
Контейнер прослуховує лише loopback-інтерфейс, тому саме nginx робить його доступним. Він також додає transport layer security (TLS). Запишіть /etc/nginx/sites-available/searxng.
server {
listen 80;
server_name search.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}sudo ln -s /etc/nginx/sites-available/searxng /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d search.example.comnginx -t виводить syntax is ok і test is successful перед перезавантаженням конфігурації. Certbot перезаписує той самий файл, щоб приймати з’єднання на 443 із сертифікатом, і додає перенаправлення з порту 80. DNS-запис для search.example.com уже має вказувати на цей сервер, оскільки центр сертифікації підтверджує володіння доменом, отримуючи файл через HTTP. Повний покроковий посібник, зокрема з поновленням сертифіката, наведено в посібнику з Certbot і nginx для Ubuntu 24.04.
Два forwarding headers — не формальність. Без X-Forwarded-For і X-Real-IP кожен запит до SearXNG містить адресу proxy, тому rate limiter бачить одного клієнта, який генерує весь трафік, і не може розрізнити відвідувачів.
Чому скриптам і агентам потрібен JSON API для пошуку
За допомогою json у formats той самий endpoint, який відображає сторінку, повертає структуровані дані.
curl -s 'http://127.0.0.1:8080/search?q=wireguard+mtu&format=json' \
| jq -r '.results[0:5][] | .url'У відповіді ви отримуєте об’єкт із масивом results. Кожен його елемент містить url, title, content і назву рушія, який надав результат, а також answers, infoboxes і suggestions. Цього достатньо, щоб передати дані summariser, link checker або циклу дослідження. Передавання таких результатів мовній моделі є складнішим, ніж здається, оскільки результати пошуку — це недовірений текст, який може містити власні інструкції. Саме це детально розглянуто в матеріалі як підключити AI-агента до свого екземпляра SearXNG.
Це важливо для будь-яких систем з агентами. Мовна модель має граничну дату навчання, тому для відповідей на запитання про поточні події їй потрібен пошук у реальному часі. Комерційні пошукові API стягують плату за кожен запит і жорстко обмежують частоту запитів. Локальний екземпляр використовує один контейнер на сервері, за який ви вже платите, а запити не залишають його. Якщо ви підключаєте інструменти до моделі, та сама логіка пояснює запуск MCP-серверів на VPS, де пошуковий інструмент зазвичай додають першим.
Для використання API діють два правила. Зробіть екземпляр приватним: прив’яжіть API до loopback-адреси або приватної мережі й дозвольте доступ лише власним хостам. Так само потрібно налаштовувати self-hosted трекер місцезнаходження для endpoint, на який телефон надсилає дані про своє розташування. Потім надсилайте запити помірно. SearXNG пересилає ваш запит до реальних пошукових систем, тому скрипт, який виконує сто запитів за секунду, фактично просить Google заблокувати ваш сервер.
Лімітер і те, що змінюється для публічного інстансу
Лімітер — це механізм захисту SearXNG від ботів. Він відстежує заголовки запитів, адреси та частоту запитів і відкидає трафік, який має ознаки автоматизованого. Для зберігання цього стану йому потрібен Valkey, тому Valkey включено до Compose-файлу.
Для приватного інстансу залиште limiter: false. Власні скрипти за визначенням генерують автоматизований трафік, тому лімітер блокував би саме ті JSON-запити, заради яких ви розгорнули інстанс. Керування доступом має виконувати reverse proxy: пара allow і deny у файлі конфігурації nginx location, HTTP basic authentication або firewall, який дозволяє підключення лише з інших ваших серверів. Якщо потрібно підключатися до приватного інстансу з ноутбука, який переходить між різними мережами, четвертим варіантом є розмістити перед ним onion-адресу v3, оскільки tor підключається до того самого loopback-порту, не відкриваючи нічого нового для інтернету.
Якщо ви відкриваєте інстанс для інших користувачів, увімкніть обидва перемикачі.
server:
limiter: true
public_instance: trueТочніше керування доступне у core-config/limiter.toml, який контейнер читає з /etc/searxng/limiter.toml. Вказуйте лише ключі, які потрібно змінити. За reverse proxy потрібно оголосити proxy, інакше лімітер вважатиме адресу nginx єдиним клієнтом, який генерує зловживання.
[botdetection]
trusted_proxies = [
'127.0.0.0/8',
'::1',
]
[botdetection.ip_limit]
link_token = truelink_token = true змушує SearXNG видавати токен, який отримує лише справжня browser session, що зупиняє більшість простих scraper-ів. Очікуйте, що протягом кількох днів публічний інстанс приверне їхню увагу. Також очікуйте помилок від пошукових рушіїв: що більше трафіку ви пересилаєте, то швидше upstream-рушії почнуть повертати CAPTCHA на адресу вашого сервера. Публічний інстанс SearXNG потребує постійного адміністрування. Приватний інстанс — ні, тому він входить до більшості коротких списків того, що варто self-hosting у 2026. Не кожен пункт у таких списках є інфраструктурою: перетворити бібліотеку Jellyfin на прогулянку магазином відеопрокату 90-х — це той самий один контейнер за тим самим блоком nginx, але він орієнтований на вечірній відпочинок, а не на робочий процес.
Чому пошук не повертає результатів
Відкрийте /stats у своєму екземплярі. Тут наведено всі пошукові рушії, їхній відсоток помилок і час відповіді. Це перше місце, яке слід перевірити, якщо результатів мало.
Якщо для рушія відображаються помилки "Access denied" або "CAPTCHA", він заблокував адресу вашого сервера. Це поширено для адрес із діапазонів дата-центрів, оскільки пошукові системи вважають, що такі адреси належать скрейперам. Після цього SearXNG на певний час призупиняє рушій, який повертає помилки, замість повторних спроб. Тому один заблокований рушій непомітно зникає з результатів. Вимкніть його в settings.yml або прийміть втрату. Це не єдині варіанти, оскільки для деяких блокувань CAPTCHA є рішення, яке зберігається після перезапуску. Інші рушії продовжують відповідати. Код 429 є неоднозначним випадком, оскільки він може надходити від вашого власного обмежувача або від upstream-рушія, який відмовляє вашому серверу. Рядок журналу показує, який саме з цих двох випадків ви маєте ще до того, як ви почнете змінювати налаштування.
Якщо всі рушії одночасно повертають помилки, контейнер не має справного вихідного розпізнавання імен або маршруту до інтернету. Перевірте це зсередини контейнера.
docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo okСистема не повідомить вас, коли ця перевірка почне завершуватися помилкою. Тому запустіть її через cron і дозвольте помилці надсилати сповіщення на телефон із вашого власного ntfy-сервера, замість того щоб чекати, доки ви помітите зменшення кількості результатів.
FAQ
Чи робить SearXNG мої пошукові запити анонімними?
SearXNG приховує вашу особу від пошукових систем, до яких надсилає запити, оскільки вони бачать запит від вашого сервера, а не від браузера. Але SearXNG не приховує запит від вашого сервера і не приховує ваш сервер від пошукових систем. В однокористувацькому екземплярі весь трафік із цієї адреси належить вам, тому сама адреса стає ідентифікатором. Трафік між браузером і вашим екземпляром захищає TLS-сертифікат. Вплив цього на конфіденційність щодо вашого ISP, оператора публічного екземпляра та самих пошукових систем описано в розділі що саме приховує SearXNG.
Чому JSON-запит повертає 403 Forbidden?
Є 2 причини, і обидві пов’язані з конфігурацією. Або json відсутній у списку formats під search: у settings.yml, що є типовим станом, або limiter увімкнений і класифікував ваш скрипт як бота. Спочатку додайте цей формат, перезапустіть із docker compose restart core, а потім повторіть запит. Якщо помилка не зникає, установіть limiter: false і керуйте доступом на reverse proxy.
Чи потрібен мені контейнер Valkey, якщо limiter вимкнений?
Залиште його запущеним. SearXNG працює без нього, але без контейнера limiter не можна буде ввімкнути пізніше. Крім того, контейнер зберігає інші короткоживучі дані. Контейнер невеликий і зберігає лише кешовані дані, тому його видалення майже нічого не заощаджує, але позбавляє вас цієї можливості.
Як оновити SearXNG?
Виконайте docker compose pull, а потім docker compose up -d у /opt/searxng. Compose повторно створює контейнери, образи яких змінилися, і не змінює ваш каталог core-config/, тому settings.yml зберігається. Оскільки use_default_settings: true об’єднує ваші ключі з типовими налаштуваннями, нові параметри від upstream отримують коректні значення замість того, щоб спричинити помилку у файлі.
Чи можуть кілька людей спільно використовувати один екземпляр?
Так. У такому випадку потрібно ввімкнути limiter і встановити public_instance: true. Налаштування зберігаються в окремому браузері кожного відвідувача, тому керувати обліковими записами не потрібно. Протягом тижня після відкриття доступу стежте за /stats, оскільки upstream-пошукові системи починають відхиляти запити від вашого сервера задовго до того, як ви помітите відсутні результати.