引子
用 Docker 部署 qBittorrent,第一件事通常是去 WebUI 装搜索插件。结果 v5.x 的搜索页面要么是空的,要么点「安装」毫无反应——这个问题我花了接近两天才彻底搞清楚,中间绕了好几个弯。
如果你遇到的是完全不同的错误(比如无法启动或下载任务 error),本文不适用。但如果你也是 Docker 部署的 qBittorrent 并且搜索插件用不了,尤其是日志里有 ImportError: No module named parse、IndentationError、NameError: name '__file__' is not defined 之类的东西,那大概率跟我是同一个坑。
报了什么
搜索插件装不上,WebUI 点安装无反应,插件列表始终为空。翻日志能看到这样的输出:
Could not parse Nova search engine capabilities, msg:
Error: Traceback (most recent call last):
File "/config/qBittorrent/nova3/nova2.py", line X, in <module>
import urllib.parse
ImportError: No module named parse
urllib.parse 是 Python 3 标准库的模块,不存在「找不到」的可能——问题不在代码本身,而在 Python 是怎么被调用的。还有其他报错混在一起出现:
IndentationError: unexpected indent
NameError: name '__file__' is not defined
这几个错误指向一个方向:Python 没有正常运行一个 .py 文件,而是进入了一个奇怪的执行模式。
定位:从日志到环境变量
先从最简单的入手:看看容器里 Python 能不能正常工作。
docker exec qbittorrent python3 --version
# 能正常输出版本号
试着运行搜索插件脚本本身:
# 直接运行
docker exec qbittorrent /usr/bin/python3 \
/config/qBittorrent/nova3/nova2.py --capabilities
# 正常输出,没有报错
这就矛盾了——手动执行没问题,qBittorrent 内部调用就出错。说明 qBittorrent 调用 Python 的方式和手动执行不一样。qBittorrent 内部用 QProcess 启动 Python,关键区别在于当前工作目录和环境变量。
查一下环境变量:
docker exec qbittorrent env | grep HOME
# HOME=/config
HOME=/config。linuxserver 系列镜像有个常见做法:把 HOME 设为 /config 以方便配置文件挂载。大部分情况下这无害,但对 Python 3.12+ 有问题。
验证一下:
# 在 HOME=/config 下执行 Python
docker exec qbittorrent sh -c "cd /config && python3 -c \"print('hello')\""
# >>> ← 直接进了交互模式!
docker exec qbittorrent sh -c "cd /tmp && python3 -c \"print('hello')\""
# hello ← 正常输出
确认了:只要工作目录在 /config(即 HOME 目录)下,Python 3.12+ 会强制进入交互模式,导致脚本执行一半就卡住。
根因:Python 3.12 的 site.py 做了什么
Python 3.12 的 site.py 有一段新增的逻辑(PEP 668 相关):启动时检查当前工作目录是否在用户 HOME 下,如果在,尝试初始化用户站点包(~/.local 等)路径。如果该目录不存在或权限不对,Python fallback 到交互模式。
在 linuxserver 的 qbittorrent 容器中:
HOME=/configsite.py发现工作目录在/config下- 尝试访问
~/.local/lib/python3.12/site-packages/——该路径不存在或不可写 - fallback 到交互模式
qBittorrent 内部调用流程:
QProcess("python3 /config/qBittorrent/nova3/nova2.py --capabilities")
→ CWD 是 /(默认),但 /config/qBittorrent/nova3/nova2.py 的目录在 HOME 下
→ site.py 检测到路径在 HOME 中 → 交互模式
→ python3 启动后显示 >>> 等待输入
→ qBittorrent 等待 stdout 输出 → 超时 → "Could not parse"
这就是手动执行(指定 cwd)和 QProcess 执行(默认 cwd)行为不一致的根本原因。

各种尝试(全部失败)
为了不让你重复走弯路,这些方案我都验证过了:
| 方案 | 结果 | 原因 |
|---|---|---|
| symlink nova3 到 /opt | ❌ 手动执行 OK,qB 内部仍失败 | QProcess 不跟随 symlink,Python 仍检测到路径在 HOME 下 |
设置 python_executable_path 配置项 |
❌ 没影响 | 只指定 Python 二进制路径,不影响环境变量 |
清除 __pycache__ 重试 |
❌ 无效 | 缓存不是根因 |
| 删除 nova3 目录让 qB 重新生成 | ❌ 新建文件仍在 /config 下 | 问题在目录级,不在文件内容 |
--tmpfs /config/qBittorrent/nova3 |
❓ 理论上可行,未验证 | 把 nova3 挂到 tmpfs 可能绕过检测,但破坏性大 |
| 修改 Dockerfile 重设 HOME | ❓ 需要自定义镜像 | 脱离 linuxserver 官方支持范围 |
结论:v5.x 在 linuxserver 镜像上搜索插件不能用,且没有干净可靠的 workaround。
降级到 v4.6.5
第零步:备份种子
ssh root@<nas-ip> \
"cp -r /path/to/qbittorrent/BT_backup /backup/qb_bt_backup"
第一步:停止并清理
ssh root@<nas-ip> "docker stop qbittorrent && docker rm qbittorrent"
config 目录可以清也可以不清——v4.x 能直接读取 v5 的配置,但如果要彻底干净,清掉也没问题:
ssh root@<nas-ip> "rm -rf /path/to/qbittorrent/config/*"
第二步:拉取 v4.6.5 镜像
# 如果需要代理
ssh root@<nas-ip> \
"HTTP_PROXY=http://<proxy-ip>:<proxy-port> HTTPS_PROXY=http://<proxy-ip>:<proxy-port> \
docker pull lscr.io/linuxserver/qbittorrent:4.6.5"
193MB 的镜像,速度看代理情况,快则几分钟,慢则半小时。
第三步:启动容器
ssh root@<nas-ip> "docker run -d --name qbittorrent \
-e WEBUI_PORT=8080 -e PUID=99 -e PGID=100 -e TZ=Asia/Shanghai \
-p 6881:6881 -p 6881:6881/udp -p 8085:8080 \
-v /path/to/qbittorrent/config:/config \
-v /path/to/downloads:/downloads \
--restart unless-stopped \
lscr.io/linuxserver/qbittorrent:4.6.5"
第四步:取临时密码 + 配置 Jackett 插件
# 获取临时密码
docker logs qbittorrent 2>&1 | grep -i 'temporary password'
# 登录并关闭 HostHeaderValidation
docker exec qbittorrent curl -s -c /tmp/c \
-d 'username=admin&password=TEMPPWD' \
'http://localhost:8080/api/v2/auth/login'
docker exec qbittorrent curl -s -b /tmp/c \
'http://localhost:8080/api/v2/app/setPreferences' \
-d 'json={"WebUI":{"HostHeaderValidation":false}}'
# 安装 Jackett 搜索插件
curl -s -b /tmp/qb \
'http://<nas-ip>:8085/api/v2/search/installPlugin' \
-d 'sources=https://raw.githubusercontent.com/qbittorrent/search-plugins/master/nova3/engines/jackett.py'
Jackett 的 URL 和 API Key 从 Jackett WebUI 右上角复制。
替代方案:Jackett Web UI
如果你不想降级,Jackett 本身有完整的 Web 搜索界面——直接在浏览器打开 Jackett 的端口(默认 9117),搜索、复制 magnet 链接、切回 qBittorrent 粘贴下载。少了「一键添加」的便利,但功能完整。
总结
这个 bug 的完整链路:
linuxserver 镜像 HOME=/config
→ Python 3.12 site.py 检测到工作目录在 HOME 下
→ 尝试初始化用户站点包失败
→ fallback 到交互模式
→ qBittorrent QProcess 调用 Python 进入 >>> 等待
→ nova2.py 不能正常解析
→ "Could not parse Nova search engine capabilities"

技术含量不算高,但层层嵌套的三个环节(Docker 镜像惯例 + Python 版本行为变化 + 内部进程调用方式)让你很难一眼看出根因。
另外说说什么时候该降级。通常不建议回退版本——安全更新和新功能都追不上。但这个情况的判断依据很简单:
- 根因在镜像环境,不在应用本身——换个环境就能正常工作的 bug,降级是合理的
- 没有其他 workaround——5 种尝试都失败,不需要继续死磕
- 功能差异明确——v4.x 和 v5.x 对 BT 下载这个核心功能没区别,差别只在搜索插件的实现方式
- v5.x 的新功能你用不上——如果 v5 没有你必须用的特性(比如 I2P 支持、新的 RSS 引擎等),降级零代价
回到最初的那句话:如果 v5 没有你非用不可的理由,v4.6.5 配合 Jackett 是目前 docker-qbittorrent 搜索功能最稳定的组合。