SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor

Cómo alojar SearXNG privado con Docker Compose

Instala SearXNG en tu VPS con Docker Compose: configura settings.yml, el limiter, nginx con TLS y una API JSON para consultar desde tus propios scripts.

Qué va a implementar

Alojar SearXNG le proporciona un motor de búsqueda privado que se ejecuta en su propio servidor. SearXNG es un metabuscador: recibe su 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 se crea ningún perfil ni se establece ninguna cookie de seguimiento, porque la única máquina que conserva su consulta es la suya. Si ha encontrado guías antiguas para algo llamado simplemente Searx, ese es el proyecto del que se bifurcó este. No recibe mantenimiento desde septiembre de 2023, cuando su propio repositorio anunció que Searx ya no se mantiene, y el único commit posterior, una nota de documentación de mayo de 2026, no cambió esa situación. Por tanto, compruebe el estado de ambos antes de seguir uno.

La pila es pequeña. Dos contenedores, un archivo de configuración y un reverse proxy. Funcionará sin problemas en un VPS pequeño, algo que no ocurre con todos los servicios autoalojados: las bibliotecas fotográficas comparadas en PhotoPrism frente a Immich establecen su consumo mínimo de RAM por el indexador, no por la aplicación web. La decisión importante es si la instancia será privada, es decir, si sólo usted y sus propios scripts podrán acceder a ella, o pública, es decir, si cualquier persona en internet podrá realizar consultas. Esa elección cambia la configuración de seguridad, así que tómela antes de escribir nada. La respuesta predeterminada es privada.

Existe un segundo motivo para ejecutar una instancia. Una instancia de SearXNG ofrece una API JSON, por lo que cualquier script o agente de IA que escriba dispondrá de una API de búsqueda propia, sin clave, sin facturación por consulta y sin mensajes sobre cuotas.

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 plugin de 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 .env

El 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 para conservar estado durante periodos breves. Monta ./core-config/ en /etc/searxng/ dentro del contenedor, por lo que toda la configuración se encuentra en ese único directorio del host.

Ahora edite .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=8080

SEARXNG_HOST=127.0.0.1 es el valor importante. Hace que el puerto publicado sea 127.0.0.1:8080:8080 en lugar de un 8080:8080 sin dirección específica. Docker publica este último en todas las direcciones, por lo que 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 publicado por Docker se inserta antes que las reglas del firewall. Este problema merece una lectura completa: los puertos publicados por Docker omiten ufw.

SEARXNG_VERSION=latest es suficiente mientras aprende. En un servidor importante, fije la etiqueta. En octubre de 2026, las etiquetas de lanzamiento se basan en fechas y tienen un formato como 2026.10.4-d48c4b555. Así, una implementación con una versión fijada se actualiza cuando usted lo decide, no cuando el registro cambia por su cuenta. La misma disciplina resulta útil para cualquier otro servicio de larga duración en el equipo. 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 evidente 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 sólo las claves que haya escrito, de modo que el archivo sea breve y siga funcionando después de las actualizaciones que añadan opciones nuevas.

Genere primero el secreto, porque el valor se escribe directamente en el archivo.

openssl rand -hex 32
use_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
    - json

secret_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, incluida la barra final. SearXNG la utiliza en los enlaces que genera. Si la deja apuntando a localhost, el enlace de "página siguiente" de un navegador remoto apuntará al propio equipo del usuario y fallará.

formats determina los tipos de salida que 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 enruta las miniaturas de los resultados a través de su servidor, de modo que los sitios que alojan esas imágenes no 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 es ese mismo contenedor.

El secreto está en un archivo de texto sin cifrar, por lo que también debe proteger el directorio que lo contiene. chmod 750 /opt/searxng impide el acceso de otros usuarios del host. También puede restringir core-config/settings.yml al modo 600 sin causar problemas: el entrypoint del contenedor oficial se inicia como root y, con FORCE_OWNERSHIP en su valor predeterminado de true, asigna el directorio de configuración montado al usuario searxng del propio contenedor antes de iniciar SearXNG, por lo que el archivo sigue siendo legible para ese usuario. El efecto secundario aparece en el host: ese mismo paso cambia el propietario de core-config/ al ID de usuario del contenedor, así que después deberá editar el archivo con sudo.

Inicie el stack y compruébelo.

cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/

docker compose ps debería mostrar ambos contenedores en estado running. El curl debería responder 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.

Configúrelo detrás de nginx con TLS

El contenedor sólo escucha en loopback. 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.com

nginx -t muestra syntax is ok y test is successful antes de recargar. Certbot modifica 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 descargando un archivo mediante 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 tanto, 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 genera 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 un array results, cuyos elementos incluyen url, title, content y el motor que proporcionó cada resultado, junto con 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 mayor de lo que parece, porque los resultados de búsqueda son texto no confiable que puede incluir sus propias instrucciones. Esto se explica en detalle en dirigir un agente de IA a su instancia de SearXNG.

Esto es importante para cualquier sistema con 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 frecuencia estrictos. Una instancia local sólo cuesta un contenedor en un servidor que ya paga, y las consultas no salen de él. Si está integrando herramientas en un modelo, el mismo razonamiento lleva a 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. Mantenga la instancia privada. Enlace el lado de la API a la dirección de loopback o a una red privada, y permita que sólo sus propios hosts accedan a ella. Es el mismo tratamiento que necesita un rastreador de ubicación autoalojado para el endpoint al que su teléfono envía su posición. Después, haga las consultas con moderación. SearXNG reenvía su solicitud a motores de búsqueda reales, por lo que un script que ejecute cien consultas por segundo está pidiendo a Google que bloquee su servidor.

El limitador y los cambios necesarios para una instancia pública

El limitador es la defensa de SearXNG contra los bots. Supervisa las cabeceras de las solicitudes, las direcciones y las tasas de solicitudes, y descarta el tráfico que parece automatizado. Necesita Valkey para almacenar ese estado, por eso el archivo 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: puede usar un par allow y deny en location de nginx, autenticación básica HTTP o un firewall que sólo permita el acceso desde sus otros servidores. Si necesita acceder a una instancia privada desde un portátil que cambia de red, colocar una dirección onion v3 delante de ella es una cuarta opción, 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: true

El control más detallado se configura 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 tratará la dirección de nginx como la del cliente abusivo.

[botdetection]
trusted_proxies = [
  '127.0.0.0/8',
  '::1',
]

[botdetection.ip_limit]
link_token = true

link_token = true hace que SearXNG emita un token que sólo obtiene 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. También pueden producirse errores de los motores, porque cuanto más tráfico reenvíe, antes empezarán los motores upstream a devolver CAPTCHAs a la dirección de su servidor. Mantener 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 por el que se pueda pasear utiliza 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 todos los motores, su tasa de errores y su tiempo de respuesta. Es el primer lugar que debe revisar cuando los resultados parecen escasos.

Un motor que aparece con 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 volver a intentarlo. Por eso, un solo motor bloqueado deja de aportar resultados sin que sea evidente. Desactívelo en settings.yml o acepte la pérdida. No son las únicas opciones, porque algunos bloqueos por CAPTCHA tienen una solución que persiste 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 causas se aplica 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 condiciones desde dentro del contenedor.

docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo ok

El sistema no le indicará cuándo esa comprobación empiece a fallar. Ejecútela desde cron y haga que un fallo envíe una alerta a su teléfono desde su propio servidor ntfy en lugar de esperar a notar que los resultados han disminuido.

FAQ

¿SearXNG hace anónimas mis búsquedas?

Oculta tu identidad a los motores que consulta, porque estos ven que tu servidor realiza la petición en lugar de tu navegador. No oculta la consulta a tu servidor ni oculta tu servidor frente a 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 certificado TLS protege el tráfico entre tu navegador y la instancia. La situación 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 petición 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á activado y ha clasificado tu script como un bot. Añade primero el formato y reinicia con docker compose restart core. Después, 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 más adelante sin ese contenedor. Además, contiene otros datos de estado de corta duración. El contenedor es pequeño y sólo almacena datos en caché, por lo que eliminarlo ahorra muy poco y elimina esa opción.

¿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 upstream reciben valores razonables en lugar de romper el archivo.

¿Pueden compartir una instancia varias personas?

Sí. En ese caso 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 upstream empiezan a rechazar tu servidor mucho antes de que notes que faltan resultados.

¿Te resultó útil este artículo?