Installer SearXNG en auto-hébergement avec Docker
Installez SearXNG sur votre VPS avec Docker Compose : settings.yml, limiter, nginx avec TLS et API JSON pour appeler la recherche depuis vos scripts.
Ce que vous allez mettre en place
L’auto-hébergement de SearXNG vous fournit un moteur de recherche privé qui fonctionne sur votre propre serveur. SearXNG est un métamoteur de recherche : il prend votre requête, interroge d’autres moteurs comme Google, Bing, DuckDuckGo et Wikipedia, puis regroupe les résultats dans une seule page. Aucun profil n’est créé et aucun cookie de suivi n’est défini, car votre requête n’est conservée que par votre propre machine. Si vous avez trouvé d’anciens guides consacrés à un projet appelé simplement Searx, il s’agit du projet dont SearXNG est un fork. Il n’est plus maintenu depuis septembre 2023, date à laquelle son propre dépôt a annoncé que Searx n’était plus maintenu. Le commit suivant, une note de documentation publiée en mai 2026, n’a rien changé à cette situation. Vous devez donc vérifier l’état des deux projets avant de suivre l’un de ces guides.
La stack est réduite. Deux conteneurs, un fichier de configuration et un reverse proxy. Elle fonctionnera sans problème sur un petit VPS. Ce n’est pas le cas de tous les services auto-hébergés : les bibliothèques photo comparées dans PhotoPrism face à Immich déterminent leur besoin minimal en RAM selon l’indexeur, et non selon l’application web. La vraie décision concerne le caractère privé ou public de l’instance. Une instance privée n’est accessible que par vous et vos propres scripts. Une instance publique peut être interrogée par n’importe qui sur Internet. Ce choix modifie les paramètres de sécurité. Faites-le avant de saisir la moindre commande. La réponse par défaut est « privée ».
Il existe une autre raison d’en exécuter une. Une instance SearXNG expose une API JSON. Tous les scripts ou agents IA que vous écrivez disposent ainsi d’une API de recherche que vous contrôlez, sans clé, sans facturation par requête et sans quota à surveiller.
Installer SearXNG avec Docker Compose
Le projet publie une image de conteneur et un fichier Compose. Récupérez les deux sur un serveur Ubuntu 24.04 vierge qui dispose déjà de Docker Engine et du plugin Compose. Si Docker est nouveau pour vous, commencez par les bases de Docker Compose sur un VPS, puis revenez ici.
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 .envLe fichier Compose définit deux services. core est SearXNG lui-même, et valkey est un magasin de données en mémoire utilisé pour la limitation de débit et l’état temporaire. Il monte ./core-config/ dans /etc/searxng/ à l’intérieur du conteneur. Toute votre configuration se trouve donc dans ce répertoire unique sur l’hôte.
Modifiez maintenant .env. Toutes les lignes de l’exemple fourni sont commentées. C’est pourquoi le conteneur démarre sur le port 8080, sur toutes les adresses. Décommentez et définissez ces trois paramètres.
SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080SEARXNG_HOST=127.0.0.1 est le paramètre important. Il fait publier le port 127.0.0.1:8080:8080 au lieu d’un simple 8080:8080. Docker publie alors le port sur toutes les adresses. Le conteneur ne répond donc que sur l’adresse loopback et Internet ne peut pas l’atteindre directement. Si vous omettez ce paramètre, le conteneur est exposé dès son démarrage, car un port Docker publié est placé avant les règles de votre pare-feu. Ce piège mérite une lecture complète : les ports Docker publiés contournent ufw.
SEARXNG_VERSION=latest convient pendant la phase d’apprentissage. Sur un serveur important, utilisez un tag fixe. En octobre 2026, les tags de release sont basés sur la date et ressemblent à 2026.10.4-d48c4b555. Un déploiement avec un tag fixe est donc mis à niveau quand vous le décidez, et non lorsque le registre change sans vous prévenir. Cette discipline est également utile pour les autres services destinés à rester actifs longtemps sur la machine. C’est pourquoi un relais RustDesk auto-hébergé utilise lui aussi des tags d’image fixes : une mise à niveau automatique d’un service d’accès distant se manifeste toujours au pire moment.
settings.yml : les éléments importants
Créez core-config/settings.yml avant le premier démarrage. use_default_settings: true indique à SearXNG de charger ses valeurs par défaut fournies avec le logiciel, puis d’appliquer uniquement les clés que vous avez écrites. Votre fichier reste ainsi court et résiste aux mises à niveau qui ajoutent de nouvelles options.
Générez d’abord le secret, car sa valeur sera inscrite directement dans le fichier.
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 signe les données de session et les jetons. La valeur par défaut fournie est la chaîne littérale ultrasecretkey. La conserver permet à toute personne qui la connaît de forger ces jetons. Remplacez-la une fois, puis ne la modifiez plus : la changer ensuite supprime toutes les préférences enregistrées.
base_url doit être l’adresse HTTPS publique, avec la barre oblique finale. SearXNG l’utilise dans les liens qu’il génère. Si vous la laissez pointer vers localhost, le lien « page suivante » ouvert dans un navigateur distant pointe vers la machine de l’utilisateur et échoue.
formats détermine les types de sortie que l’endpoint web produira. json ne figure pas dans la liste par défaut. Une requête JSON renvoie donc une erreur 403 tant que vous ne l’avez pas ajouté. image_proxy: true fait transiter les vignettes des résultats par votre serveur. Les sites qui hébergent ces images ne voient ainsi jamais les adresses de vos visiteurs.
Le valkey.url utilise le nom d’hôte valkey, car c’est le nom du service dans le fichier Compose. Compose place les deux conteneurs sur un même réseau, où les noms de service sont résolus. Si vous le faites pointer vers localhost, le limiteur échoue, car localhost désigne ce conteneur à l’intérieur du conteneur core.
Le secret se trouve dans un fichier en clair. Protégez donc aussi le répertoire qui le contient. chmod 750 /opt/searxng empêche les autres utilisateurs de l’hôte d’y accéder. Vous pouvez également renforcer core-config/settings.yml avec le mode 600 sans rien casser : l’entrypoint du conteneur officiel démarre en tant que root et, avec FORCE_OWNERSHIP conservé à sa valeur par défaut true, attribue le répertoire de configuration monté à l’utilisateur searxng du conteneur avant de démarrer SearXNG. Le fichier reste ainsi lisible par ce dernier. La conséquence apparaît sur l’hôte : cette même étape modifie le propriétaire de core-config/ et lui attribue l’ID utilisateur du conteneur. Prévoyez donc de modifier ensuite le fichier avec sudo.
Démarrez la stack et vérifiez son état.
cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/docker compose ps doit afficher les deux conteneurs avec l’état running. Le curl doit répondre sur HTTP/1.1 200 OK. S’il ne renvoie rien, consultez docker compose logs core, car une erreur YAML dans settings.yml y apparaît sous la forme d’une erreur d’analyse indiquant la ligne concernée.
Placez-le derrière nginx avec TLS
Le conteneur écoute uniquement sur la loopback. nginx le rend donc accessible et assure également la sécurité de la couche transport (TLS). Écrivez /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 affiche syntax is ok et test is successful avant le rechargement. Certbot réécrit le même fichier pour écouter sur 443 avec un certificat et ajoute une redirection depuis le port 80. L’enregistrement DNS de search.example.com doit déjà pointer vers ce serveur, car l’autorité de certification vérifie la propriété du domaine en récupérant un fichier via HTTP. La procédure complète, renouvellement compris, se trouve dans le guide Certbot et nginx pour Ubuntu 24.04.
Les deux en-têtes de transmission ne sont pas décoratifs. Sans X-Forwarded-For et X-Real-IP, chaque requête qui arrive à SearXNG contient l’adresse du proxy. Le limiteur de débit voit donc un seul client générer tout le trafic et ne peut pas distinguer les visiteurs.
Pourquoi les scripts et les agents ont besoin d’une API de recherche JSON
Avec json dans formats, le même endpoint qui génère la page renvoie des données structurées.
curl -s 'http://127.0.0.1:8080/search?q=wireguard+mtu&format=json' \
| jq -r '.results[0:5][] | .url'Vous recevez un objet contenant un tableau results. Chaque entrée contient url, title, content et le moteur qui a fourni le résultat, ainsi que answers, infoboxes et suggestions. Cela suffit pour alimenter un outil de synthèse, un vérificateur de liens ou une boucle de recherche. Transmettre ces résultats à un modèle de langage représente une étape plus importante qu’il n’y paraît, car les résultats de recherche sont du texte non fiable qui peut contenir ses propres instructions. C’est précisément le fonctionnement détaillé de la connexion d’un agent IA à votre instance SearXNG.
Cela concerne tout ce qui prend la forme d’un agent. Un modèle de langage a une date limite de connaissances. Il a donc besoin d’une recherche en temps réel pour répondre aux questions sur l’actualité. Les API de recherche commerciales facturent chaque requête et appliquent des limites de débit strictes. Une instance locale ne coûte qu’un conteneur sur un serveur que vous payez déjà, et les requêtes ne le quittent jamais. Si vous connectez des outils à un modèle, le même raisonnement conduit à exécuter des serveurs MCP sur un VPS, un outil de recherche étant généralement le premier que l’on ajoute.
Deux règles pour l’utilisation de l’API. Gardez l’instance privée. Liez donc l’API à l’adresse loopback ou à un réseau privé, et autorisez uniquement vos propres hôtes à y accéder. C’est le même traitement que celui dont un outil de suivi de position auto-hébergé a besoin pour l’endpoint auquel votre téléphone envoie sa position. Ensuite, effectuez les requêtes avec modération. SearXNG transmet votre requête aux vrais moteurs de recherche. Un script qui exécute cent requêtes par seconde demande donc à Google de bloquer votre serveur.
Le limiteur et ce qui change pour une instance publique
Le limiteur est le mécanisme de défense de SearXNG contre les bots. Il surveille les en-têtes des requêtes, les adresses et les taux de requêtes, puis bloque le trafic qui semble automatisé. Il a besoin de Valkey pour conserver cet état, d’où sa présence dans le fichier Compose.
Sur une instance privée, conservez limiter: false. Par définition, vos propres scripts génèrent du trafic automatisé. Le limiteur bloquerait donc précisément les appels JSON pour lesquels vous avez créé l’instance. Le contrôle d’accès relève plutôt du reverse proxy : une paire allow et deny dans la configuration nginx location, une authentification HTTP basic ou un firewall qui n’autorise que vos autres serveurs. Si vous devez accéder à une instance privée depuis un ordinateur portable qui change de réseau, placez une adresse onion v3 devant elle constitue une quatrième option, car Tor se connecte au même port loopback sans rien exposer de plus sur Internet.
Si vous publiez l’instance pour d’autres utilisateurs, activez les deux options.
server:
limiter: true
public_instance: trueLe réglage détaillé se trouve dans core-config/limiter.toml, que le conteneur lit à partir de /etc/searxng/limiter.toml. N’écrivez que les clés que vous voulez modifier. Derrière un proxy, vous devez déclarer ce proxy. Sinon, le limiteur considère l’adresse de nginx comme celle du client abusif.
[botdetection]
trusted_proxies = [
'127.0.0.0/8',
'::1',
]
[botdetection.ip_limit]
link_token = truelink_token = true demande à SearXNG d’émettre un token que seule une véritable session de navigateur récupérera, ce qui bloque la plupart des scrapers simples. Une instance publique commencera probablement à les attirer en quelques jours. Attendez-vous aussi à des erreurs des moteurs, car plus vous leur transmettez de trafic, plus vite les moteurs upstream commenceront à renvoyer des CAPTCHA à l’adresse de votre serveur. Une instance SearXNG publique demande un suivi continu. Une instance privée ne demande pas ce travail, ce qui explique sa présence dans la plupart des listes succinctes de services qui méritent d’être auto-hébergés en 2026. Toutes les entrées de ces listes ne sont d’ailleurs pas de l’infrastructure : reconstruire une bibliothèque Jellyfin sous la forme d’un vidéoclub des années 90 où l’on peut se promener revient à utiliser le même conteneur derrière le même bloc nginx, mais orienté vers une soirée plutôt que vers un workflow.
Pourquoi les recherches ne renvoient aucun résultat
Ouvrez /stats sur votre instance. Cette page répertorie chaque moteur avec son taux d’erreur et son temps de réponse. C’est le premier endroit à consulter lorsque les résultats semblent trop peu nombreux.
Un moteur qui affiche des erreurs « Access denied » ou « CAPTCHA » a bloqué l’adresse de votre serveur. C’est fréquent pour les adresses appartenant à des plages de datacenters, car les moteurs de recherche supposent qu’elles sont utilisées par des scrapers. SearXNG suspend alors le moteur en échec pendant un certain temps au lieu de réessayer. Un moteur bloqué disparaît donc discrètement de vos résultats. Désactivez-le dans settings.yml ou acceptez cette perte. Ce ne sont pas les deux seules possibilités, car certains blocages par CAPTCHA peuvent être corrigés définitivement, y compris après un redémarrage. Les autres moteurs continuent de répondre. Le code 429 est un cas ambigu, car il peut provenir de votre propre limiteur ou d’un moteur en amont qui refuse votre serveur. La ligne du journal indique lequel de ces deux cas se présente avant que vous ne modifiiez la configuration.
Si tous les moteurs échouent en même temps, le conteneur ne dispose pas d’une résolution de noms sortante fonctionnelle ou n’a pas de route vers Internet. Effectuez ce test depuis l’intérieur du conteneur.
docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo okRien sur le serveur ne vous indiquera que ce contrôle commence à échouer. Exécutez-le donc depuis cron et laissez un échec envoyer une alerte sur votre téléphone depuis votre propre serveur ntfy, au lieu d’attendre de constater que les résultats sont devenus trop peu nombreux.
FAQ
SearXNG rend-il mes recherches anonymes ?
Il masque votre identité aux moteurs interrogés, car ceux-ci voient votre serveur envoyer la requête, et non votre navigateur. Il ne masque pas la requête à votre serveur et ne masque pas votre serveur aux moteurs. Sur une instance utilisée par une seule personne, tout le trafic provenant de cette adresse est le vôtre : l’adresse devient donc elle-même un identifiant. Le trafic entre votre navigateur et votre instance est protégé par le certificat TLS. Les conséquences vis-à-vis de votre FAI, de l’opérateur d’une instance publique et des moteurs eux-mêmes sont expliquées dans ce que SearXNG masque réellement.
Pourquoi une requête JSON renvoie-t-elle 403 Forbidden ?
Deux causes sont possibles, et elles concernent toutes deux la configuration. Soit json est absent de la liste formats sous search: dans settings.yml, ce qui est l’état par défaut, soit le limiter est activé et a classé votre script comme un bot. Ajoutez d’abord le format, redémarrez avec docker compose restart core, puis réessayez. Si l’échec persiste, définissez limiter: false et contrôlez l’accès au niveau du reverse proxy.
Ai-je besoin du conteneur Valkey si je laisse le limiter désactivé ?
Laissez-le fonctionner. SearXNG fonctionne sans lui, mais le limiter ne pourra pas être activé ultérieurement, et le conteneur stocke également d’autres états temporaires. Le conteneur est léger et ne stocke que des données en cache. Sa suppression économise donc très peu de ressources et vous prive de cette possibilité.
Comment mettre SearXNG à jour ?
Exécutez docker compose pull, puis docker compose up -d dans /opt/searxng. Compose recrée tout conteneur dont l’image a changé et laisse votre répertoire core-config/ intact. settings.yml est donc conservé. Comme use_default_settings: true fusionne vos clés avec les valeurs par défaut fournies, les options ajoutées en amont reçoivent des valeurs cohérentes au lieu de rendre le fichier invalide.
Plusieurs personnes peuvent-elles partager une même instance ?
Oui. C’est dans ce cas que vous activez le limiter et définissez public_instance: true. Les préférences sont stockées dans le navigateur de chaque visiteur. Il n’y a donc aucun compte à gérer. Surveillez /stats pendant une semaine après l’ouverture de l’instance, car les moteurs en amont commencent à rejeter votre serveur bien avant que vous ne remarquiez l’absence de résultats.