Installer SearXNG en privé avec Docker Compose
Déployez SearXNG sur votre VPS avec Docker Compose, settings.yml, limiter et nginx TLS, puis appelez son API JSON depuis vos propres scripts.
Ce que vous allez mettre en place
L’auto-hébergement de SearXNG vous fournit un moteur de recherche privé qui s’exécute 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 sur 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. Ce projet n’a reçu aucun commit depuis 2023. Vérifiez donc l’état des deux projets avant de suivre l’un de ces guides.
La stack est légère. Deux conteneurs, un fichier de configuration et un reverse proxy suffisent. Elle peut facilement partager un petit VPS, ce qui n’est pas le cas de tous les services auto-hébergés : les bibliothèques photo comparées dans PhotoPrism et Immich fixent leur besoin minimal en RAM sur la base de l’indexeur, et non de l’application web. La vraie décision consiste à déterminer si l’instance est privée, c’est-à-dire accessible uniquement par vous et vos propres scripts, ou publique, c’est-à-dire interrogeable par toute personne sur Internet. Ce choix modifie les paramètres de sécurité. Faites-le avant de saisir quoi que ce soit. La réponse par défaut est « privée ».
Il existe une seconde raison d’en exécuter une. Une instance SearXNG expose une API JSON. Chaque script ou agent IA que vous écrivez dispose ainsi d’une API de recherche que vous contrôlez, sans clé, sans facturation par requête et sans message concernant un quota.
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 limiter le débit et conserver un état de courte durée. Il monte ./core-config/ dans /etc/searxng/ à l’intérieur du conteneur. Ainsi, toute la configuration se trouve dans ce répertoire unique sur l’hôte.
Modifiez maintenant .env. Chaque ligne de l’exemple fourni est commentée. C’est pourquoi le conteneur démarre sur le port 8080, sur toutes les adresses. Décommentez et définissez ces trois lignes.
SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080SEARXNG_HOST=127.0.0.1 est le paramètre important. Il publie le port 127.0.0.1:8080:8080 au lieu de [::]:8080:8080. Le conteneur répond ainsi uniquement sur l’adresse loopback, et Internet ne peut pas y accéder directement. Si vous ignorez ce paramètre, le conteneur est exposé dès son démarrage, car un port Docker publié est inséré 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, verrouillez le tag. En juillet 2026, les tags de release sont basés sur la date et ressemblent à 2026.3.25-541c6c3cb. Un déploiement utilisant un tag verrouillé se met à niveau lorsque vous le décidez, et non lorsque le registry est modifié sans intervention de votre part. Cette discipline est également utile pour les autres services destinés à fonctionner longtemps sur le serveur. C’est pourquoi un relais RustDesk auto-hébergé verrouille lui aussi ses tags d’image : 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 directement inscrite 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 tokens. La valeur par défaut fournie est la chaîne littérale ultrasecretkey. Si vous la conservez, toute personne qui la connaît peut falsifier ces tokens. 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 le slash final. C’est cette adresse que SearXNG écrit 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 du lecteur et échoue.
formats détermine les types de sortie que l’endpoint web produit. json ne figure pas dans la liste par défaut. Une requête JSON renvoie donc une erreur 403 tant que vous ne l’ajoutez pas. image_proxy: true fait transiter les miniatures des résultats par votre serveur. Les sites qui hébergent ces images ne voient donc jamais les adresses de vos visiteurs.
Le valkey.url utilise le hostname valkey, car il s’agit du 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 définissez sur localhost, le rate limiter é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 le répertoire qui le contient plutôt que le fichier lui-même. chmod 750 /opt/searxng empêche les autres utilisateurs de l’hôte d’y accéder. Ne réduisez pas core-config/settings.yml au mode 600 : le conteneur s’exécute avec son propre utilisateur non privilégié, et un fichier qu’il ne peut pas lire empêche complètement SearXNG de démarrer.
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 dans l’état running. Le curl doit répondre sur HTTP/1.1 200 OK. S’il ne répond pas, 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 ajoute é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é en récupérant un fichier via HTTP. La procédure complète, y compris le renouvellement, 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 l’a fournie, 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 constitue 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 le fonctionnement détaillé de la connexion d’un agent IA à votre instance SearXNG.
Cela concerne tout ce qui s’appuie sur des agents. Un modèle de langage possède une date limite pour ses données d’entraînement. 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 se limite à un conteneur sur un serveur que vous payez déjà, et les requêtes ne le quittent jamais. Si vous intégrez 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 utiliser l’API. Gardez l’instance privée. Liez donc le service API à l’adresse loopback ou à un réseau privé, et autorisez uniquement vos propres hôtes à y accéder. Envoyez ensuite les requêtes à un rythme modéré. SearXNG transmet votre requête aux moteurs de recherche réels. Un script qui exécute cent requêtes par seconde demande donc à Google de bloquer votre serveur.
Le limiter et ce qui change pour une instance publique
Le limiter est le mécanisme de défense de SearXNG contre les bots. Il surveille les en-têtes des requêtes, les adresses et le rythme des 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, laissez limiter: false. Vos propres scripts génèrent par définition du trafic automatisé. Le limiter 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 le location de nginx, une authentification HTTP basic ou un pare-feu 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, placer une adresse onion v3 devant celle-ci est une quatrième option. Tor se connecte alors 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: trueUn contrôle plus précis se trouve dans core-config/limiter.toml, que le conteneur lit à partir de /etc/searxng/limiter.toml. Écrivez uniquement les clés que vous voulez modifier. Derrière un proxy, vous devez déclarer celui-ci. Sinon, le limiter 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ère, ce qui bloque la plupart des scrapers simples. Une instance publique attirera probablement ce type de trafic en quelques jours. Attendez-vous aussi à des erreurs des moteurs. Plus vous relayez de trafic, plus vite les moteurs en amont commenceront à renvoyer des CAPTCHA à l’adresse de votre serveur. Une instance SearXNG publique demande une maintenance continue. Ce n’est pas le cas d’une instance privée, raison pour laquelle elle figure dans la plupart des listes courtes de services qui valent la peine d’être auto-hébergés en 2026. Toutes les entrées de ces listes ne sont pas non plus des composants d’infrastructure : reconstruire une bibliothèque Jellyfin sous la forme d’un vidéoclub des années 90 où l’on peut déambuler utilise le même conteneur derrière le même bloc nginx, mais vise une soirée plutôt qu’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 insuffisants.
Un moteur qui affiche des erreurs « Access denied » ou « CAPTCHA » a bloqué l’adresse de votre serveur. C’est fréquent avec 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, si bien qu’un moteur bloqué disparaît discrètement de vos résultats. Désactivez-le dans settings.yml ou acceptez cette perte. Ce ne sont toutefois pas les deux seules options, car certains blocages par CAPTCHA peuvent être corrigés de façon persistante après un redémarrage. Les autres moteurs continuent de répondre. Une erreur 429 est plus difficile à interpréter, car elle 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 commenciez à modifier la configuration.
Si tous les moteurs échouent en même temps, le conteneur n’a pas de résolution de noms sortante fonctionnelle ou n’a pas de route vers Internet. Testez ces éléments depuis l’intérieur du conteneur.
docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo okLe système ne vous signalera pas 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, plutôt que d’attendre de constater que les résultats sont devenus insuffisants.
FAQ
SearXNG rend-il mes recherches anonymes ?
Il masque votre identité auprès des moteurs qu’il interroge, car ceux-ci voient votre serveur effectuer la requête, et non votre navigateur. En revanche, il ne masque pas la requête à votre serveur et ne masque pas votre serveur auprès des 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 l’identifiant. Le trafic entre votre navigateur et votre instance est protégé par le certificat TLS. Les implications 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 peut fonctionner sans lui, mais le limiter ne pourra pas être activé ultérieurement, et le conteneur stocke également d’autres données d’état temporaires. Le conteneur est peu volumineux et ne stocke que des données mises en cache. Sa suppression apporte donc un gain très limité 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 les conteneurs 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 provoquer une erreur dans le fichier.
Plusieurs personnes peuvent-elles partager une même instance ?
Oui. C’est le cas où 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.