Como hospedar seu próprio SearXNG com Docker Compose
Instale o SearXNG em um VPS com Docker Compose, configure settings.yml e o limiter, use nginx com TLS e exponha uma API JSON para seus scripts.
O que vai ser criado
Hospedar o SearXNG dá-lhe um motor de pesquisa privado que é executado no seu próprio servidor. O SearXNG é um motor de metapesquisa: recebe a sua consulta, consulta outros motores, como Google, Bing, DuckDuckGo e Wikipedia, e combina os resultados numa única página. Não é criado nenhum perfil nem é definido nenhum cookie de rastreio, porque a única máquina que guarda a sua consulta é a sua. Se encontrou guias antigos sobre algo chamado simplesmente Searx, esse é o projeto do qual este foi criado por fork e não recebe nenhum commit desde 2023, por isso verifique o estado de ambos antes de seguir um deles.
A stack é pequena. São dois contentores, um ficheiro de definições e um reverse proxy. Funcionará sem problemas num VPS pequeno, o que não acontece com todos os serviços self-hosted: as bibliotecas de fotografias comparadas em PhotoPrism e Immich definem o consumo mínimo de RAM pelo indexador, e não pela aplicação web. A decisão principal é saber se a instância será privada, ou seja, acessível apenas por si e pelos seus próprios scripts, ou pública, ou seja, acessível a qualquer pessoa na internet. Essa escolha altera as definições de segurança, por isso faça-a antes de introduzir qualquer comando. A resposta predefinida é privada.
Há uma segunda razão para executar uma instância. Uma instância SearXNG disponibiliza JSON, por isso qualquer script ou agente de IA que escreva terá uma API de pesquisa sua, sem chave, cobrança por consulta ou mensagens sobre quotas.
Instalar o SearXNG com Docker Compose
O projeto publica uma imagem de contentor e um ficheiro Compose. Transfira ambos para um servidor Ubuntu 24.04 novo que já tenha o Docker Engine e o plugin Compose. Se o Docker ainda for novidade para si, comece por Noções básicas de Docker Compose num VPS e volte depois.
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 .envO ficheiro Compose define dois serviços. core é o próprio SearXNG e valkey é um armazenamento de dados em memória usado para limitar a taxa de pedidos e guardar estado de curta duração. O ficheiro monta ./core-config/ em /etc/searxng/ dentro do contentor, por isso tudo o que configurar fica nesse único diretório no host.
Agora edite .env. Todas as linhas do exemplo fornecido estão comentadas. Por isso, o contentor inicia na porta 8080 em todos os endereços. Retire os comentários e defina estes três valores.
SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080SEARXNG_HOST=127.0.0.1 é a definição importante. Faz com que a porta publicada seja 127.0.0.1:8080:8080 em vez de [::]:8080:8080. Assim, o contentor responde apenas no endereço de loopback e a Internet não consegue aceder-lhe diretamente. Se ignorar esta definição, o contentor fica exposto assim que inicia, porque uma porta publicada pelo Docker é inserida antes das regras da firewall. Vale a pena ler esta armadilha por completo: as portas publicadas pelo Docker ignoram o ufw.
SEARXNG_VERSION=latest é adequado enquanto está a aprender. Num servidor importante, fixe a tag. Em julho de 2026, as tags de release são baseadas na data e têm um formato semelhante a 2026.3.25-541c6c3cb. Assim, uma implementação com versão fixa é atualizada quando decidir, e não quando o registry for alterado. A mesma disciplina é útil para qualquer outro serviço de longa duração no servidor. Por isso, um relay RustDesk self-hosted também fixa as tags das imagens: uma atualização não supervisionada de um serviço de acesso remoto manifesta-se no pior momento possível.
settings.yml: as partes importantes
Crie core-config/settings.yml antes da primeira inicialização. use_default_settings: true instrui o SearXNG a carregar os valores predefinidos fornecidos pelo próprio programa e, depois, aplicar apenas as chaves que escreveu. Assim, o ficheiro mantém-se curto e continua compatível com atualizações que adicionem novas opções.
Gere primeiro o segredo, porque o valor será colocado diretamente no ficheiro.
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 assina os dados de sessão e dos tokens. O valor predefinido fornecido pelo programa é a cadeia literal ultrasecretkey. Se a deixar, qualquer pessoa que conheça esse valor predefinido pode forjar esses tokens. Substitua-a uma vez e não a altere depois: alterá-la elimina todas as preferências guardadas.
base_url tem de ser o endereço HTTPS público, incluindo a barra final. É esse endereço que o SearXNG escreve nas ligações que gera. Se o deixar apontado para localhost, a ligação "página seguinte" num navegador remoto apontará para a própria máquina do utilizador e falhará.
formats define os tipos de saída que o endpoint web produzirá. json não faz parte da lista predefinida, por isso um pedido JSON devolve 403 até que o adicione. image_proxy: true encaminha as miniaturas dos resultados através do seu servidor, impedindo que os sites que alojam essas imagens vejam os endereços dos seus visitantes.
O valkey.url usa o nome de anfitrião valkey porque esse é o nome do serviço no ficheiro Compose, e o Compose coloca ambos os contentores na mesma rede, onde os nomes dos serviços são resolvidos. Aponte-o para localhost e o limitador falhará, porque, dentro do contentor core, localhost identifica esse próprio contentor.
O segredo fica num ficheiro simples. Por isso, proteja o diretório que o contém, e não apenas o ficheiro. chmod 750 /opt/searxng impede o acesso dos outros utilizadores do anfitrião. Não restrinja core-config/settings.yml para o modo 600: o contentor é executado com o seu próprio utilizador sem privilégios, e um ficheiro que não consiga ler impede o SearXNG de iniciar.
Inicie a stack e verifique-a.
cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/docker compose ps deve mostrar ambos os contentores no estado running. O curl deve responder com HTTP/1.1 200 OK. Se não responder, consulte docker compose logs core, porque um erro YAML em settings.yml aparece aí como um erro de análise que indica a linha.
Coloque-o atrás do nginx com TLS
O contentor escuta apenas na interface de loopback. Por isso, o nginx permite torná-lo acessível e também adiciona a segurança da camada de transporte (TLS). Escreva /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 apresenta syntax is ok e test is successful antes de recarregar a configuração. O Certbot reescreve o mesmo ficheiro para escutar na porta 443 com um certificado e adiciona um redirecionamento da porta 80. O registo DNS de search.example.com já tem de apontar para este servidor, porque a autoridade de certificação comprova a propriedade ao obter um ficheiro através de HTTP. O procedimento completo, incluindo a renovação, está em guia do Certbot e nginx para Ubuntu 24.04.
Os dois cabeçalhos de encaminhamento não são decorativos. Sem X-Forwarded-For e X-Real-IP, todos os pedidos que chegam ao SearXNG transportam o endereço do proxy. Assim, o limitador de taxa considera que um único cliente gera todo o tráfego e não consegue distinguir os visitantes.
Por que scripts e agentes precisam de uma API de pesquisa JSON
Com json em formats, o mesmo endpoint que apresenta a página devolve dados estruturados.
curl -s 'http://127.0.0.1:8080/search?q=wireguard+mtu&format=json' \
| jq -r '.results[0:5][] | .url'É devolvido um objeto com um array results, em que cada entrada contém url, title, content e o motor que forneceu o resultado, juntamente com answers, infoboxes e suggestions. Isso é suficiente para alimentar um sumarizador, um verificador de links ou um ciclo de pesquisa. Entregar esses resultados a um modelo de linguagem é um passo maior do que parece, porque os resultados da pesquisa são texto não confiável e podem conter instruções próprias. É isso que apontar um agente de IA para a sua instância SearXNG aborda em detalhe.
Isto é importante para qualquer aplicação com agentes. Um modelo de linguagem tem uma data-limite de treino. Por isso, precisa de pesquisa em tempo real para responder a perguntas sobre o presente. As APIs comerciais de pesquisa cobram por consulta e aplicam limites de taxa rigorosos. Uma instância local usa um contentor num servidor que já paga e as consultas nunca saem desse servidor. Se estiver a ligar ferramentas a um modelo, o mesmo princípio aplica-se a executar servidores MCP num VPS, onde uma ferramenta de pesquisa costuma ser a primeira que as pessoas adicionam.
Há duas regras para usar a API. Mantenha a instância privada. Para isso, associe o lado da API ao endereço de loopback ou a uma rede privada e permita o acesso apenas aos seus próprios hosts. Depois, faça as consultas com moderação. O SearXNG encaminha o seu pedido para motores de pesquisa reais. Assim, um script que execute cem consultas por segundo está a pedir ao Google para bloquear o seu servidor.
O limitador e o que muda numa instância pública
O limitador é a defesa do SearXNG contra bots. Ele monitoriza cabeçalhos de pedidos, endereços e taxas de pedidos, e descarta o tráfego que parece automatizado. Precisa do Valkey para armazenar esse estado, motivo pelo qual o ficheiro Compose o inclui.
Numa instância privada, mantenha limiter: false. Os seus próprios scripts são, por definição, tráfego automatizado, portanto o limitador bloquearia precisamente as chamadas JSON para as quais criou a instância. O controlo de acesso deve ficar a cargo do reverse proxy: um par allow e deny no location do nginx, autenticação HTTP básica ou uma firewall que permita ligações apenas dos seus outros servidores. Se precisar de aceder a uma instância privada a partir de um portátil que muda de rede, colocar um endereço onion v3 à frente dela é uma quarta opção, porque o tor liga-se à mesma porta de loopback sem expor nada de novo à internet.
Se publicar a instância para outras pessoas, ative ambas as opções.
server:
limiter: true
public_instance: trueO controlo mais detalhado está em core-config/limiter.toml, que o contentor lê de /etc/searxng/limiter.toml. Escreva apenas as chaves que pretende alterar. Atrás de um proxy, tem de declarar o proxy; caso contrário, o limitador considera o endereço do nginx como o cliente abusivo.
[botdetection]
trusted_proxies = [
'127.0.0.0/8',
'::1',
]
[botdetection.ip_limit]
link_token = truelink_token = true faz o SearXNG emitir um token que apenas uma sessão de navegador real irá obter, o que bloqueia a maioria dos scrapers simples. Espere que uma instância pública os atraia em poucos dias. Espere também erros dos motores, porque, quanto mais tráfego encaminhar, mais cedo os motores upstream começarão a devolver CAPTCHAs ao endereço do seu servidor. Uma instância pública do SearXNG exige manutenção contínua. Uma privada não exige, motivo pelo qual aparece em grande parte das listas curtas de coisas que vale a pena alojar autonomamente em 2026. Nem todas as entradas dessas listas são infraestrutura: reconstruir uma biblioteca do Jellyfin como uma videoclube dos anos 90 onde se pode passear usa o mesmo contentor atrás do mesmo bloco do nginx, orientado para uma noite em vez de um fluxo de trabalho.
Por que as pesquisas não retornam resultados
Abra /stats na sua instância. Esse ficheiro lista cada motor com a respetiva taxa de erro e tempo de resposta. É o primeiro local a consultar quando os resultados parecem insuficientes.
Um mecanismo que apresenta erros como "Access denied" ou "CAPTCHA" bloqueou o endereço do seu servidor. Isso é comum em endereços de intervalos de centros de dados, porque os motores de pesquisa presumem que pertencem a scrapers. O SearXNG suspende então o mecanismo que falhou durante algum tempo, em vez de repetir o pedido. Assim, um mecanismo bloqueado deixa de aparecer silenciosamente nos seus resultados. Desative-o em settings.yml ou aceite a perda. Essas não são as únicas opções, porque alguns bloqueios por CAPTCHA têm uma correção que permanece ativa depois de um reboot. Os restantes mecanismos continuam a responder. Um erro 429 é o caso ambíguo, porque pode ser causado pelo seu próprio limitador ou por um mecanismo upstream que recusou o seu servidor, e a linha do log indica qual das duas situações está a ocorrer antes de começar a alterar as definições.
Se todos os motores falharem ao mesmo tempo, o contentor não tem resolução de nomes de saída funcional ou não tem uma rota para a Internet. Teste isso a partir do interior do contentor.
docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo okNada no sistema lhe indicará quando essa verificação começar a falhar. Por isso, execute-a pelo cron e deixe uma falha enviar um alerta para o seu telemóvel a partir do seu próprio servidor ntfy, em vez de esperar até notar que os resultados ficaram insuficientes.
FAQ
O SearXNG torna as minhas pesquisas anónimas?
O SearXNG oculta a sua identidade dos motores que consulta, porque estes veem o seu servidor a fazer o pedido em vez do seu navegador. Não oculta a consulta do seu servidor e não oculta o seu servidor dos motores. Numa instância para um único utilizador, todo o tráfego desse endereço é seu, pelo que o próprio endereço se torna o identificador. O tráfego entre o seu navegador e a instância é protegido pelo certificado TLS. As implicações para a sua privacidade perante o ISP, o operador de uma instância pública e os próprios motores são explicadas em o que o SearXNG oculta realmente.
Por que motivo um pedido JSON devolve 403 Forbidden?
Há duas causas, e ambas estão relacionadas com a configuração. Ou json está em falta na lista formats, em search:, dentro de settings.yml, que é o estado predefinido, ou o limitador está ativo e classificou o seu script como bot. Adicione primeiro o formato, reinicie com docker compose restart core e tente novamente. Se continuar a falhar, defina limiter: false e controle o acesso no reverse proxy.
Preciso do contentor Valkey se mantiver o limitador desativado?
Mantenha-o em execução. O SearXNG funciona sem ele, mas o limitador não poderá ser ativado posteriormente sem o contentor, que também armazena outro estado de curta duração. O contentor é pequeno e armazena apenas dados em cache. Removê-lo poupa muito pouco e elimina essa opção.
Como atualizo o SearXNG?
Execute docker compose pull e depois docker compose up -d em /opt/searxng. O Compose recria qualquer contentor cuja imagem tenha sido alterada e deixa o diretório core-config/ intacto, pelo que settings.yml permanece. Como use_default_settings: true combina as suas chaves com as predefinições fornecidas, as opções adicionadas a montante são aplicadas com valores adequados em vez de fazerem o ficheiro falhar.
Várias pessoas podem partilhar uma instância?
Sim. Esse é o caso em que deve ativar o limitador e definir public_instance: true. As preferências são armazenadas no navegador de cada visitante, pelo que não há contas para gerir. Monitorize /stats durante uma semana depois de disponibilizar a instância, porque os motores a montante começam a rejeitar o seu servidor muito antes de notar resultados em falta.