如何使用 Docker Compose 自行托管 SearXNG 搜索引擎
在 VPS 上部署私有 SearXNG 实例的完整指南。涵盖 settings.yml 配置、Nginx TLS 反向代理设置以及如何通过 JSON API 为您的脚本和 AI Agent 提供免密钥的搜索服务。
您将构建的内容
自行托管 SearXNG,可以在自己的服务器上运行私有搜索引擎。SearXNG 是一个元搜索引擎:它接收您的查询,向 Google、Bing、DuckDuckGo 和 Wikipedia 等其他引擎发起请求,然后将返回的结果合并到一个结果页面中。系统不会建立用户画像,也不会设置跟踪 Cookie,因为唯一保存您查询内容的机器就是您自己的服务器。如果您找到的是介绍一个简称为 Searx 的项目的旧教程,那么 SearXNG 就是从该项目分叉而来。该项目自 September 2023 起已无人维护;其代码仓库当时宣布 Searx 不再维护,之后唯一的一次提交是 May 2026 的文档说明,也没有改变这一状态。因此,在选择教程前,请确认这两个项目的状态。
整个技术栈很小:两个容器、一个设置文件和一个反向代理。它可以轻松与其他服务共用一台小型 VPS,但并非所有自行托管的服务都如此:PhotoPrism 与 Immich 的对比中提到的照片库,其最低 RAM 要求取决于索引器,而不是 Web 应用。真正需要决定的是实例应保持私有,还是公开提供服务。私有表示只有您和自己编写的脚本可以访问;公开表示互联网上的任何人都可以查询。这个选择会改变安全设置,因此请在输入任何命令前先确定。默认应选择私有模式。
运行实例还有第二个原因。SearXNG 实例提供 JSON 接口,因此您编写的任何脚本或 AI agent 都可以使用一个由您控制的搜索 API,无需密钥、无需按查询付费,也没有配额通知邮件。
使用 Docker Compose 安装 SearXNG
该项目发布了容器镜像和 Compose 文件。将这两者拉取到一台全新的 Ubuntu 24.04 服务器上。该服务器应已安装 Docker Engine 和 Compose 插件。如果您不熟悉 Docker,请先阅读VPS 上的 Docker Compose 基础知识,然后再继续。
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 .envCompose 文件定义了两个服务。core 是 SearXNG 本身,valkey 是用于速率限制和短期状态存储的内存数据存储。它将 ./core-config/ 挂载到容器内的 /etc/searxng/,因此您配置的所有内容都位于主机上的这一个目录中。
现在编辑 .env。随附示例中的每一行都已注释掉,因此容器会在所有地址上的 8080 端口启动。取消注释并设置下面这三项。
SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080SEARXNG_HOST=127.0.0.1 是关键配置。它将发布端口设置为 127.0.0.1:8080:8080,而不是仅写入 8080:8080。后者会让 Docker 在所有地址上发布端口,因此容器只能通过回环地址响应,互联网无法直接访问它。跳过此设置后,容器启动时就会暴露,因为 Docker 发布的端口会插入到防火墙规则之前。请完整阅读这个容易踩坑的细节:已发布的 Docker 端口会绕过 ufw。
在学习期间,SEARXNG_VERSION=latest 足够使用。在您重视的服务器上,应固定标签。截至 2026 年 10 月,发布标签采用基于日期的格式,例如 2026.10.4-d48c4b555。这样,部署只会在您决定时升级,而不会因注册表发生变化而自动升级。对服务器上其他需要长期运行的服务,也应采用相同的管理方式。这正是自托管 RustDesk 中继服务也固定镜像标签的原因:远程访问服务无人值守升级,往往会在最不合适的时候暴露出来。
settings.yml:需要关注的部分
在首次启动前创建 core-config/settings.yml。use_default_settings: true 会让 SearXNG 先加载自身随附的默认配置,然后只应用你写入的键,因此文件可以保持简短,并且在升级添加新选项后仍能正常工作。
先生成密钥,因为该值会直接写入文件。
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 用于签名会话和令牌数据。随附的默认值是字面字符串 ultrasecretkey;如果保留该值,任何知道默认值的人都可以伪造这些令牌。替换一次后不要再修改:之后更改会丢弃所有已保存的偏好设置。
base_url 必须是公开的 HTTPS 地址,并保留末尾斜杠。SearXNG 会将它写入生成的链接中。如果保留为 localhost,远程浏览器中的“下一页”链接会指向读者自己的计算机,并导致访问失败。
formats 决定 Web 端点生成哪些输出类型。默认列表中不包含 json,因此添加它之前,JSON 请求会返回 403。image_proxy: true 会通过你的服务器转发结果缩略图,因此托管这些图片的网站看不到访问者的地址。
valkey.url 使用主机名 valkey,因为这是 Compose 文件中的服务名称;Compose 会将两个容器放在同一网络中,并解析服务名称。将其指向 localhost 会导致限流器失败,因为在 core 容器内部,localhost 指向该容器自身。
密钥存储在普通文件中,因此也要保护其所在目录。chmod 750 /opt/searxng 可阻止主机上的其他用户访问。你还可以将 core-config/settings.yml 的权限收紧为模式 600,且不会造成问题:官方容器的入口点以 root 身份启动;在 FORCE_OWNERSHIP 保持默认值 true 时,它会在 SearXNG 启动前,将挂载的配置目录交给容器自身的 searxng 用户,因此该用户仍能读取文件。其副作用会出现在主机上:同一步骤会将 core-config/ 的所有者改为容器用户 ID,因此之后应使用 sudo 编辑该文件。
启动堆栈并检查。
cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/docker compose ps 应显示两个状态为 running 的容器。curl 应返回 HTTP/1.1 200 OK。如果没有返回内容,请查看 docker compose logs core,因为 settings.yml 中的 YAML 错误会在那里显示为解析错误,并指出对应的行。
配置 nginx 并启用 TLS
容器仅监听 loopback,因此需要通过 nginx 才能访问该服务;nginx 还负责提供传输层安全性(TLS)。写入 /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 会在重新加载前打印 syntax is ok 和 test is successful。Certbot 会重写同一个文件,使其监听带证书的 443 端口,并添加从 80 端口到 HTTPS 的重定向。search.example.com 的 DNS 记录必须已经指向此服务器,因为证书颁发机构会通过 HTTP 获取文件来验证所有权。完整步骤(包括续期)请参阅 适用于 Ubuntu 24.04 的 Certbot 和 nginx 指南。
这两个转发标头不是装饰。缺少 X-Forwarded-For 和 X-Real-IP 时,到达 SearXNG 的每个请求都会携带代理服务器地址,因此速率限制器会认为所有流量都来自同一个客户端,无法区分不同访问者。
为什么脚本和代理需要 JSON 搜索 API
在 formats 中使用 json 后,渲染页面的同一端点也会返回结构化数据。
curl -s 'http://127.0.0.1:8080/search?q=wireguard+mtu&format=json' \
| jq -r '.results[0:5][] | .url'返回结果是一个包含 results 数组的对象。每个条目都包含 url、title、content、提供结果的搜索引擎,以及 answers、infoboxes 和 suggestions。这些数据足以供摘要生成器、链接检查器或研究循环使用。将这些结果交给语言模型并不像看起来那么简单,因为搜索结果是不受信任的文本,其中可能包含自身的指令。让 AI 代理连接到你的 SearXNG 实例 会详细处理这个问题。
这对任何代理类应用都很重要。语言模型存在训练数据截止时间,因此需要实时搜索来回答当前问题;而商业搜索 API 按查询次数收费,并且通常有严格的速率限制。在你已经付费使用的服务器上运行本地实例只需一个容器,查询也不会离开该服务器。如果你要将工具接入模型,同样的原因也适用于 在 VPS 上运行 MCP 服务器,搜索工具通常是人们首先添加的工具。如果你的模型调用经过 new-api 或 LiteLLM 这类网关中转,要注意网关只转发请求,不会替模型上网;为 LLM 网关接入联网搜索对比了在客户端、网关和上游厂商三处接入搜索的成本,并给出了让 LiteLLM 调用私有 SearXNG 实例的配置。
API 使用需要遵循两条规则。首先,保持实例私有。将 API 绑定到回环地址或私有网络,只允许你自己的主机访问它。这与 自托管位置跟踪器 的处理方式相同:手机上报位置的端点也应采用这种配置。其次,查询请求要适度。SearXNG 会将请求转发给真实的搜索引擎,因此每秒执行 100 次查询的脚本,实际上是在请求 Google 封禁你的服务器。
限流器及公共实例的变更事项
限流器是 SearXNG 的机器人防御机制。它监控请求头、IP 地址和请求速率,并丢弃看起来是自动化的流量。它需要 Valkey 来保存该状态,这就是 Compose 文件中包含它的原因。
对于私有实例,请保持 limiter: false。您的脚本本质上就是自动化流量,因此限流器会拦截您构建该实例所调用的 JSON 请求。访问控制应由反向代理负责:在 nginx location 中设置 allow 和 deny 对、使用 HTTP 基本认证,或配置防火墙仅允许您的其他服务器访问。如果您需要从经常切换网络的笔记本电脑访问私有实例,在其前端添加 v3 onion 地址是第四种选择,因为 Tor 会连接到同一个回环端口,而不会向互联网暴露任何新内容。
如果您确实要向公众发布该实例,请开启这两个开关。
server:
limiter: true
public_instance: true更精细的控制位于 core-config/limiter.toml,容器会在 /etc/searxng/limiter.toml 读取该文件。您只需写入想要更改的键值。在代理后端,您必须声明代理,否则限流器会将您的 nginx 地址视为唯一的滥用客户端。
[botdetection]
trusted_proxies = [
'127.0.0.0/8',
'::1',
]
[botdetection.ip_limit]
link_token = truelink_token = true 会使 SearXNG 发出一个只有真实浏览器会话才会获取的令牌,这能阻止大多数简单的爬虫。预计公共实例在几天内就会吸引这些爬虫。您也可能会遇到引擎错误,因为转发的流量越多,上游引擎就越快开始向您的服务器地址返回 CAPTCHA。公共 SearXNG 实例是一项持续性的工作。私有实例则不然,这就是它出现在大多数2026 年值得自托管的项目短名单上的原因。这些名单上的条目并非全是基础设施:将 Jellyfin 库重构为 90 年代风格的租赁店也是在同一个 nginx 块后运行同一个容器,它指向的是休闲时光而非工作流。
搜索没有返回结果的原因
在您的实例上打开 /stats。该页面列出每个引擎的错误率和响应时间。当结果过少时,应首先查看这里。
如果某个引擎显示 “Access denied” 或 “CAPTCHA” 错误,说明该引擎已阻止您的服务器地址。数据中心地址段中的地址经常遇到这种情况,因为搜索引擎通常认为这些地址属于爬虫。SearXNG 随后会暂时停用失败的引擎,而不是继续重试。因此,一个被阻止的引擎会在不明显提示的情况下从结果中消失。您可以在 settings.yml 中禁用它,也可以接受结果减少。不过,这并不是仅有的两种处理方式,因为部分 CAPTCHA 阻止可以通过配置修复,并在重启后继续生效。其余引擎仍会返回结果。429 是一种无法直接判断原因的情况,因为它可能来自您自己的限流器,也可能来自拒绝您服务器的上游引擎;在修改设置前,请先查看日志行,以确定具体是哪一种情况。
如果所有引擎同时失败,说明容器无法正常进行出站名称解析,或无法访问互联网。请从容器内部进行测试。
docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo ok主机上的任何组件都不会告知您该检查何时开始失败,因此请通过 cron 定期运行测试,并让失败通过您自己的 ntfy 服务器向手机发送告警,不要等到发现结果变少后再处理。
FAQ
SearXNG 会让我的搜索匿名吗?
它会对所查询的搜索引擎隐藏您的身份,因为这些引擎看到的是您的服务器发起请求,而不是您的浏览器。它不会对您的服务器隐藏查询内容,也不会对这些引擎隐藏您的服务器。在单用户实例中,来自该地址的所有流量都属于您,因此该地址本身就会成为标识符。您的浏览器与实例之间的流量受 TLS 证书保护。针对您的 ISP、公共实例的运营者以及搜索引擎,SearXNG 实际隐藏了哪些信息,请参阅SearXNG 实际隐藏的内容。
为什么 JSON 请求返回 403 Forbidden?
有两种原因,且都属于配置问题。第一种是 settings.yml 中 search: 下的 formats 列表缺少 json,这是默认状态。第二种是 limiter 已启用,并将您的脚本识别为 bot。先添加该格式,然后使用 docker compose restart core 重启,再重试。如果仍然失败,请设置 limiter: false,并改为在反向代理中控制访问。
如果我一直关闭 limiter,还需要 Valkey 容器吗?
请保持其运行。SearXNG 没有它也能工作,但没有它就无法在以后启用 limiter;它还会保存其他短期状态。该容器很小,只存储缓存数据,因此删除它几乎节省不了空间,却会失去以后启用 limiter 的选择。
如何更新 SearXNG?
在 /opt/searxng 中依次运行 docker compose pull 和 docker compose up -d。如果镜像发生变化,Compose 会重新创建相应容器,并保持您的 core-config/ 目录不变,因此 settings.yml 会继续存在。由于 use_default_settings: true 会将您的密钥合并到随软件发布的默认值之上,上游新增的选项会获得合理值,不会导致文件失效。
多人可以共用一个实例吗?
可以。这种情况下,您应启用 limiter,并设置 public_instance: true。偏好设置保存在每位访问者自己的浏览器中,因此无需管理账户。开放访问后一周内请监控 /stats,因为上游搜索引擎通常会在您发现搜索结果缺失之前很久,就开始拒绝来自您的服务器的请求。