Cómo alojar SearXNG privado con Docker Compose
Instala SearXNG en tu VPS con Docker Compose, configura settings.yml y el limiter, añade nginx con TLS y usa la API JSON desde tus propios scripts.
Qué se va a crear
Alojar SearXNG en tu propio servidor te proporciona un motor de búsqueda privado. SearXNG es un metabuscador: recibe tu consulta, la envía a otros motores como Google, Bing, DuckDuckGo y Wikipedia, y combina las respuestas en una sola página de resultados. No crea ningún perfil ni establece cookies de seguimiento, porque la única máquina que conserva tu consulta es la tuya. Si has encontrado guías antiguas para un proyecto llamado simplemente Searx, ese es el proyecto del que deriva este fork y no ha recibido ningún commit desde 2023, así que comprueba el estado de ambos antes de seguir una de las dos guías.
La pila es pequeña. Consta de dos contenedores, un archivo de configuración y un reverse proxy. Funcionará sin problemas en un VPS pequeño. Esto no ocurre con todos los servicios autoalojados: las bibliotecas de fotos comparadas en PhotoPrism frente a Immich establecen su consumo mínimo de RAM por el indexador y no por la aplicación web. La decisión importante es si la instancia será privada, es decir, si sólo tú y tus propios scripts podrán acceder a ella, o pública, es decir, si cualquier persona en Internet podrá realizar consultas. Esta elección cambia la configuración de seguridad, así que tómala antes de escribir nada. La respuesta predeterminada es privada.
Hay una segunda razón para ejecutar una instancia. SearXNG ofrece una API JSON, así que cualquier script o agente de IA que escribas tendrá una API de búsqueda bajo tu control, sin clave, sin facturación por consulta y sin mensajes de límite de cuota.
Instalar SearXNG con Docker Compose
El proyecto publica una imagen de contenedor y un archivo Compose. Descargue ambos en un servidor Ubuntu 24.04 nuevo que ya tenga Docker Engine y el complemento Compose. Si Docker es nuevo para usted, empiece por Conceptos básicos de Docker Compose en un VPS y vuelva después.
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 .envEl archivo Compose define dos servicios. core es SearXNG y valkey es un almacén de datos en memoria que se usa para limitar la tasa y conservar estados de corta duración. Monta ./core-config/ en /etc/searxng/ dentro del contenedor, por lo que toda la configuración se almacena en ese único directorio del host.
Edite ahora .env. Todas las líneas del ejemplo incluido están comentadas. Por eso el contenedor se inicia en el puerto 8080 en todas las direcciones. Quite los comentarios y establezca estos tres valores.
SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080SEARXNG_HOST=127.0.0.1 es el ajuste importante. Hace que el puerto publicado sea 127.0.0.1:8080:8080 en lugar de [::]:8080:8080. Así, el contenedor sólo responde en la dirección de loopback y no se puede acceder a él directamente desde Internet. Si omite este ajuste, el contenedor queda expuesto en cuanto se inicia, porque un puerto de Docker publicado se inserta antes que las reglas del firewall. Conviene leer esta trampa completa: los puertos publicados de Docker omiten ufw.
SEARXNG_VERSION=latest está bien mientras aprende. En un servidor importante, fije la etiqueta. En julio de 2026, las etiquetas de versión se basan en fechas y tienen un formato como 2026.3.25-541c6c3cb. Así, una implementación con una etiqueta fija se actualiza cuando usted lo decide, no cuando el registro cambia sin aviso. La misma disciplina resulta útil para cualquier otro servicio de larga duración en el servidor. Por eso un relay de RustDesk autohospedado también fija sus etiquetas de imagen: una actualización desatendida de un servicio de acceso remoto se hace visible en el peor momento posible.
settings.yml: las partes importantes
Cree core-config/settings.yml antes del primer arranque. use_default_settings: true indica a SearXNG que cargue sus valores predeterminados incluidos y aplique después sólo las claves que haya escrito, de modo que el archivo sea breve y siga funcionando tras las actualizaciones que añadan opciones nuevas.
Genere primero el secreto, porque el valor se escribe directamente en el archivo.
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 firma los datos de sesión y los tokens. El valor predeterminado incluido es la cadena literal ultrasecretkey. Si la deja, cualquiera que conozca ese valor predeterminado puede falsificar esos tokens. Sustitúyala una vez y no la cambie después: cambiarla elimina todas las preferencias guardadas.
base_url debe ser la dirección HTTPS pública, con la barra final. SearXNG la escribe en los enlaces que genera. Si la deja apuntando a localhost, el enlace «página siguiente» de un navegador remoto apunta al propio equipo del lector y falla.
formats decide qué tipos de salida producirá el endpoint web. json no está en la lista predeterminada, por lo que una petición JSON devuelve 403 hasta que lo añada. image_proxy: true encamina las miniaturas de los resultados a través de su servidor, de modo que los sitios que alojan esas imágenes nunca ven las direcciones de sus visitantes.
El valkey.url usa el nombre de host valkey porque ese es el nombre del servicio en el archivo Compose, y Compose coloca ambos contenedores en una red donde los nombres de servicio se resuelven. Si apunta a localhost, el limitador falla, porque dentro del contenedor core localhost corresponde a ese contenedor.
El secreto está en un archivo sin cifrar, así que proteja el directorio que lo contiene, no el archivo. chmod 750 /opt/searxng impide el acceso de otros usuarios del host. No restrinja core-config/settings.yml al modo 600: el contenedor se ejecuta con su propio usuario sin privilegios, y si no puede leer el archivo, SearXNG no arranca.
Inicie la pila y compruébela.
cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/docker compose ps debe mostrar ambos contenedores en estado running. curl debe responder con HTTP/1.1 200 OK. Si no responde, lea docker compose logs core, porque un error de YAML en settings.yml aparece allí como un error de análisis que indica la línea.
Colóquelo detrás de nginx con TLS
El contenedor sólo escucha en loopback, por lo que nginx permite acceder a él y también añade seguridad de la capa de transporte (TLS). Escriba /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 muestra syntax is ok y test is successful antes de volver a cargar la configuración. Certbot reescribe el mismo archivo para escuchar en 443 con un certificado y añade una redirección desde el puerto 80. El registro DNS de search.example.com ya debe apuntar a este servidor, porque la autoridad certificadora demuestra la propiedad mediante la descarga de un archivo por HTTP. La guía completa, incluida la renovación, está en la guía de Certbot y nginx para Ubuntu 24.04.
Las dos cabeceras de reenvío no son decorativas. Sin X-Forwarded-For y X-Real-IP, cada petición que llega a SearXNG lleva la dirección del proxy, por lo que el limitador de tasa ve a un solo cliente generando todo el tráfico y no puede distinguir a los visitantes.
Por qué los scripts y los agentes necesitan una API de búsqueda JSON
Con json en formats, el mismo endpoint que muestra la página devuelve datos estructurados.
curl -s 'http://127.0.0.1:8080/search?q=wireguard+mtu&format=json' \
| jq -r '.results[0:5][] | .url'Recibe un objeto con una matriz results. Cada entrada incluye url, title, content y el motor que la proporcionó, además de answers, infoboxes y suggestions. Esto basta para alimentar un resumidor, un comprobador de enlaces o un ciclo de investigación. Pasar esos resultados a un modelo de lenguaje es un paso más importante de lo que parece, porque los resultados de búsqueda son texto no fiable que puede contener instrucciones propias. Esto se explica en detalle en cómo dirigir un agente de IA a tu instancia de SearXNG.
Esto es importante para cualquier sistema basado en agentes. Un modelo de lenguaje tiene una fecha de corte de entrenamiento, por lo que necesita búsquedas en tiempo real para responder preguntas sobre la actualidad. Además, las API de búsqueda comerciales cobran por consulta y aplican límites de tasa estrictos. Una instancia local ocupa un contenedor en un servidor que ya pagas y las consultas nunca salen de él. Si conectas herramientas a un modelo, el mismo razonamiento sustenta ejecutar servidores MCP en un VPS, donde una herramienta de búsqueda suele ser la primera que se añade.
Hay dos reglas para usar la API. Mantén la instancia privada: vincula el lado de la API a la dirección de loopback o a una red privada y permite que sólo tus propios hosts accedan a ella. Después, consulta con moderación. SearXNG reenvía tu solicitud a motores de búsqueda reales, por lo que un script que ejecute cien consultas por segundo está pidiendo a Google que bloquee tu servidor.
El limitador y qué cambia en una instancia pública
El limitador es la defensa contra bots de SearXNG. Supervisa las cabeceras de las solicitudes, las direcciones y las tasas de solicitudes, y descarta el tráfico que parece automatizado. Necesita Valkey para conservar ese estado, por eso el archivo de Compose lo incluye.
En una instancia privada, mantenga limiter: false. Sus propios scripts son tráfico automatizado por definición, por lo que el limitador bloquearía precisamente las llamadas JSON para las que creó la instancia. El control de acceso corresponde al reverse proxy: un par allow y deny en location de nginx, la autenticación básica HTTP o un firewall que sólo admita conexiones de sus otros servidores. Si necesita acceder a una instancia privada desde un portátil que cambia de red, anteponga una dirección onion v3, porque Tor se conecta al mismo puerto de loopback sin exponer nada nuevo a Internet.
Si publica la instancia para otras personas, active ambos interruptores.
server:
limiter: true
public_instance: trueEl control más preciso está en core-config/limiter.toml, que el contenedor lee desde /etc/searxng/limiter.toml. Escriba sólo las claves que quiera cambiar. Detrás de un proxy debe declarar el proxy; de lo contrario, el limitador considera que la dirección de nginx es la del único cliente abusivo.
[botdetection]
trusted_proxies = [
'127.0.0.0/8',
'::1',
]
[botdetection.ip_limit]
link_token = truelink_token = true hace que SearXNG emita un token que sólo obtendrá una sesión de navegador real, lo que detiene a la mayoría de los scrapers sencillos. Espere que una instancia pública los atraiga en pocos días. Espere también errores de los motores, porque cuanto más tráfico reenvíe, antes los motores ascendentes empezarán a devolver CAPTCHAs a la dirección de su servidor. Una instancia pública de SearXNG requiere trabajo continuo. Una instancia privada no, por eso aparece en la mayoría de las listas breves de cosas que merece la pena alojar por cuenta propia en 2026. Además, no todas las entradas de esas listas son infraestructura: reconstruir una biblioteca de Jellyfin como un videoclub de alquiler de los años 90 que se pueda recorrer es el mismo contenedor detrás del mismo bloque de nginx, orientado a una velada en lugar de a un flujo de trabajo.
Por qué las búsquedas no devuelven resultados
Abra /stats en su instancia. Muestra cada motor con su tasa de errores y tiempo de respuesta. Es el primer lugar que debe revisar cuando los resultados parecen escasos.
Un motor que muestra errores "Access denied" o "CAPTCHA" ha bloqueado la dirección de su servidor. Esto es habitual en direcciones de rangos de centros de datos, porque los motores de búsqueda suponen que pertenecen a scrapers. SearXNG suspende entonces el motor que falla durante un periodo, en lugar de reintentarlo. Por eso, un motor bloqueado desaparece de los resultados sin llamar la atención. Desactívelo en settings.yml o acepte perderlo. No son las únicas opciones, porque algunos bloqueos por CAPTCHA tienen una solución que se mantiene tras reiniciar. Los demás motores siguen respondiendo. Un error 429 es el caso ambiguo, porque puede proceder de su propio limitador o de un motor ascendente que rechaza su servidor. La línea del registro indica cuál de las dos situaciones se está produciendo antes de que empiece a cambiar la configuración.
Si todos los motores fallan a la vez, el contenedor no tiene una resolución de nombres saliente operativa o no tiene una ruta a Internet. Pruebe ambas cosas desde dentro del contenedor.
docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo okNada en el servidor le indicará cuándo empieza a fallar esa comprobación. Ejecútela desde cron y permita que un fallo envíe una alerta al teléfono desde su propio servidor ntfy en lugar de esperar hasta darse cuenta de que los resultados se han reducido.
FAQ
¿SearXNG hace anónimas mis búsquedas?
Oculta tu identidad a los motores que consulta, porque estos ven que tu servidor realiza la solicitud en lugar de tu navegador. No oculta la consulta a tu servidor ni oculta tu servidor ante esos motores. En una instancia para un solo usuario, todo el tráfico de esa dirección es tuyo, por lo que la propia dirección se convierte en el identificador. El tráfico entre tu navegador y tu instancia está protegido por el certificado TLS. La información sobre cómo te afecta esto frente a tu ISP, al operador de una instancia pública y a los propios motores se explica en qué oculta realmente SearXNG.
¿Por qué una solicitud JSON devuelve 403 Forbidden?
Hay dos causas, y ambas son de configuración. O bien falta json en la lista formats de search: dentro de settings.yml, que es el estado predeterminado, o bien el limitador está activo y ha clasificado tu script como un bot. Añade primero el formato, reinicia con docker compose restart core y vuelve a intentarlo. Si sigue fallando, establece limiter: false y controla el acceso en el reverse proxy.
¿Necesito el contenedor de Valkey si mantengo desactivado el limitador?
Déjalo en ejecución. SearXNG funciona sin él, pero el limitador no se puede activar posteriormente sin ese contenedor, que también almacena otros datos temporales. El contenedor es pequeño y sólo almacena datos en caché, por lo que eliminarlo ahorra muy poco y elimina esa posibilidad.
¿Cómo actualizo SearXNG?
Ejecuta docker compose pull y después docker compose up -d en /opt/searxng. Compose recrea cualquier contenedor cuya imagen haya cambiado y deja intacto el directorio core-config/, por lo que settings.yml se conserva. Como use_default_settings: true combina tus claves con los valores predeterminados incluidos en la versión distribuida, las opciones añadidas por el proyecto llegan con valores razonables en lugar de invalidar el archivo.
¿Pueden varias personas compartir una instancia?
Sí. Ese es el caso en el que debes activar el limitador y establecer public_instance: true. Las preferencias se almacenan en el navegador de cada visitante, por lo que no hay cuentas que administrar. Monitoriza /stats durante una semana después de abrir el acceso, porque los motores externos empiezan a rechazar tu servidor mucho antes de que notes que faltan resultados.