家用 NAS 四件套部署手册:qBittorrent + Jellyfin + Cloudreve + MoviePilot(arm64 / Debian)
这是一份可以照着一步步装的手册:在一台 Debian(arm64)小主机上搭建「PT/BT 下载 + 影视墙 + 自动化整理 + 私有网盘」四件套,并用 nginx 做统一的 SSL 反向代理对外访问。
四个服务里 qBittorrent、Jellyfin、Cloudreve 走裸机(apt / 静态二进制 + systemd),只有 MoviePilot 走 Docker。文中所有域名、IP、端口、账号、密码都用 <占位符> 或示例值,请替换成你自己的。
这套组合分别干什么
| 服务 | 作用 | 官网 / 下载 | 形态 | 默认端口 |
|---|---|---|---|---|
| qBittorrent | BT/PT 下载器,带 WebUI,负责把资源下载到 downloads |
官网 https://www.qbittorrent.org/;Debian 直接 apt install qbittorrent-nox |
裸机 | 8080(WebUI) / 6881(BT) |
| Jellyfin | 媒体服务器 / 影视墙,刮削海报、整理、串流播放 library 里的影片 |
官网 https://jellyfin.org/;官方 apt 源 https://repo.jellyfin.org | 裸机 | 8096 |
| Cloudreve | 自建网盘,文件管理 / 分享 / 多存储策略,做文档照片备份入口 | 官网 https://cloudreve.org/;GitHub https://github.com/cloudreve/cloudreve;文档 https://docs.cloudreve.org | 裸机(静态二进制) | 5212 |
| MoviePilot | 自动化整理:订阅/RSS → 调用 qBittorrent 下载 → 硬链接整理进媒体库 → 刮削 → 通知 | GitHub https://github.com/jxxghp/moviepilot;Wiki https://wiki.movie-pilot.org;镜像 jxxghp/moviepilot-v2 |
Docker | 3000 |
数据流:qBittorrent 下载到 /data/media/downloads → MoviePilot 用硬链接把完成的影片整理进 /data/media/library(不占双倍空间)→ Jellyfin 指向 library 播放。Cloudreve 用独立目录做网盘,不接管媒体库(它会改写文件名、去重共享 blob,和 Jellyfin 冲突)。
硬链接的前提:下载目录和媒体库必须在同一文件系统,所以全部放在同一个 /data 下。
前置:目录与权限规划
假设登录用户 uid/gid = 1000(下文 <user> 指你的登录用户名,<group> 指 uid 1000 对应的组名,用 getent group 1000 查)。
1 | # 建目录,全部归属 uid 1000 |
目录结构:
1 | /data |
装完 Jellyfin 后,把它的系统用户加进 <group>,让它能读 /data(详见下文 Jellyfin 部分)。
一、qBittorrent(下载器)
安装
Debian 源里就有,qbittorrent-nox 是无 GUI 的服务器版:
1 | sudo apt update |
启动(用包自带的模板 unit)
包内自带 qbittorrent-nox@.service 模板,User=%i 会以「实例名」这个用户身份运行。用你的登录用户名做实例名,UID 就天然一致:
1 | sudo systemctl enable --now qbittorrent-nox@<user>.service |
配置文件
配置在 ~/.config/qBittorrent/qBittorrent.conf(即运行用户的家目录)。建议在首次启动前就写好,这样它一开始就只监听本地、下载路径也对。关键项:
1 | [BitTorrent] |
改完 sudo systemctl restart qbittorrent-nox@<user> 生效。
首次登录密码
qBittorrent 5.x 没有默认口令,首次启动会随机生成一个临时密码打到日志里(重启会变):
1 | sudo journalctl -u qbittorrent-nox@<user>.service --no-pager | grep -i "临时密码\|temporary password" |
用 admin + 该临时密码登录 WebUI,立刻在「选项 → Web 用户界面」改成永久强密码。
二、Jellyfin(影视墙 / 媒体服务器)
安装(官方 apt 源,含 arm64)
Debian 自带源里的 Jellyfin 可能旧,用官方源:
1 | sudo mkdir -p /etc/apt/keyrings |
jellyfin-ffmpeg8是硬件加速的关键,它带h264_v4l2m2m/hevc_v4l2m2m等硬解/硬编器;Debian 自带的 ffmpeg 往往没编这些后端。装完可自检:
/usr/lib/jellyfin-ffmpeg/ffmpeg -hide_banner -codecs | grep v4l2
目录与权限
- 配置:
/etc/jellyfin(system.xml、encoding.xml) - 数据/数据库:
/var/lib/jellyfin - 运行用户:
jellyfin(装包自动创建,getent passwd jellyfin看 uid)
让 Jellyfin 能读媒体库——把 jellyfin 用户加进 <group>(uid 1000 的组),改完重启:
1 | sudo usermod -aG <group> jellyfin # 注意是“组名”,不是数字 GID |
若要用硬件解码设备(/dev/video*,属 video 组),确认 jellyfin 也在 video 组(Debian 包通常已自动加,id jellyfin 核对)。
转码临时目录(可选但推荐)
默认转码临时文件在 /var/lib/jellyfin/transcodes。建议指到数据盘,方便配额/清理,且写满时不波及系统分区。编辑 /etc/jellyfin/encoding.xml,在根节点内加一行(改前先 sudo systemctl stop jellyfin,改完再 start):
1 | <TranscodingTempPath>/data/media/.transcode</TranscodingTempPath> |
并让该目录归 jellyfin 可写:
1 | sudo chown jellyfin:<group> /data/media/.transcode |
WebUI 配置
浏览器打开 http://<内网IP>:8096 走首次向导:建管理员 → 添加媒体库,路径指向 /data/media/library。
硬件加速:控制台 → 播放 → 硬件加速。arm64 的 VPU 通常只有 8-bit H.264/HEVC 能硬解,10-bit/VP9/AV1 建议走软解;刚装好可先保持「None」验证直通播放正常,再开 v4l2m2m 只测 8-bit。主策略是原盘直出 + remux,转码当兜底。
三、Cloudreve(自建网盘)
下载
Cloudreve 是单个静态 Go 二进制,零依赖。从 GitHub Releases 按架构下载(arm64 选 _linux_arm64,x86 选 _linux_amd64):
1 | sudo mkdir -p /opt/cloudreve && cd /opt/cloudreve |
配置文件 conf.ini
放到 /data/config/cloudreve/conf.ini:
1 | [System] |
数据目录:Cloudreve 的 SQLite 数据库和 uploads 默认落在可执行文件所在目录的
data/(即/opt/cloudreve/data)。/opt通常是 root 属主,而服务以 uid 1000 运行会写不进去。想让数据落到/data,做个软链接即可:
1 sudo ln -sfn /data/config/cloudreve /opt/cloudreve/data
systemd unit
/etc/systemd/system/cloudreve.service:
1 | [Unit] |
1 | sudo systemctl daemon-reload |
启动子命令是
cloudreve server(不是-d),-c指定 conf 路径,默认端口 5212。
首次建管理员
v4 不打印默认口令。打开 Web 走首次安装页创建管理员——注册的第一个账号自动进入 Admin 组,所以服务起来后第一时间把管理员建掉。加密功能默认关闭,建议保持关闭(开启后媒体文件对 Jellyfin/MP 变不可读,且 master key 丢了数据无法恢复)。
四、MoviePilot(Docker)
装 Docker
1 | sudo apt install -y docker.io docker-compose |
docker-compose.yml
放到 /data/config/moviepilot/docker-compose.yml:
1 | services: |
逐项要点:
network_mode: host:容器共用宿主网络栈。好处是容器内的127.0.0.1就是宿主,能直接用宿主的本地代理(PROXY_HOST);缺点是ports:映射无效,MP 直接监听宿主的 3000 端口。PUID/PGID=1000:最容易错的一项。MP 用它决定容器内写文件的属主。设成 0(root)会产出 root 属主文件,后续别的程序覆盖不了;必须和/data属主一致(这里 1000)。volumes的原路径映射:/data/media/downloads:/data/media/downloads这种「左右同名」是硬链接能成立的前提。硬链接是同一文件系统内的 inode 别名,如果写成/downloads:/downloads这类别名,容器内生成的链接路径在宿主上不存在,整理会失败。MOVIEPILOT_AUTO_UPDATE=false:关掉容器内自更新,升级用手动docker compose pull && docker compose up -d,更可控。- docker.sock 不用挂:只有想让容器「自更新/自重启」才需要挂
/var/run/docker.sock;本方案关了自更新,不挂更安全(少一条容器内 root 控制宿主 Docker 的通道)。
启动
1 | cd /data/config/moviepilot |
Web 后台:http://<内网IP>:3000,用 SUPERUSER/SUPERUSER_PASSWORD 登录。进去后配置 TMDB/站点、下载器(连 qBittorrent)、目录(下载目录 /data/media/downloads、媒体库 /data/media/library),再开硬链接整理。
五、nginx 反向代理(统一 SSL 公网入口)
四个服务都只监听本地(或内网),公网访问统一走 nginx 的 SSL 端口。这样只需在路由器上转发几个 nginx 端口,服务本身不暴露。
前提:nginx 已装(sudo apt install nginx),且有一张覆盖你域名的证书(Let’s Encrypt / 自有证书均可)。下面假设证书在 /etc/nginx/certs/fullchain.pem 和 privkey.pem,域名 nas.example.com,公网端口用示例值 44301~44304(换成你自己的)。
WebSocket 升级 map(全局一次)
新建 /etc/nginx/conf.d/nas-upgrade-map.conf:
1 | map $http_upgrade $nas_connection_upgrade { |
通用 server 块模板
以 Jellyfin 为例,新建 /etc/nginx/sites-available/nas-jellyfin:
1 | server { |
启用:
1 | sudo ln -s /etc/nginx/sites-available/nas-jellyfin /etc/nginx/sites-enabled/ |
其余三个照抄,只改端口和 proxy_pass 后端:
| 站点文件 | 公网端口(示例) | proxy_pass 后端 |
|---|---|---|
| nas-jellyfin | 44301 | http://127.0.0.1:8096 |
| nas-qbittorrent | 44302 | http://127.0.0.1:8080 |
| nas-moviepilot | 44303 | http://127.0.0.1:3000 |
| nas-cloudreve | 44304 | http://127.0.0.1:5212 |
Cloudreve 那份额外加一行
proxy_request_buffering off;(大文件上传流式转发,不先落盘)。
qBittorrent 反代的额外配置(否则登录报“禁止访问”)
qBittorrent 有两道防护,在非标准端口反代下会互相冲突,导致浏览器登录被拒:
- Host 头校验:带端口的 Host(如
nas.example.com:44302)会被判为不可信; - CSRF 校验:浏览器
Origin头带端口,要求转发的Host也带端口才匹配。
解决办法(登录 qBittorrent Web API 设置,或直接在 qBittorrent.conf 的 [Preferences] 里写):
1 | WebUI\HostHeaderValidation=false ; 关掉 Host 头校验(服务只绑本地,风险可控) |
同时 nginx 那份 qBittorrent 配置里,把 Host 和 X-Forwarded-Host 都改成带端口的 $http_host(让 CSRF 的 Origin 比对通过):
1 | proxy_set_header Host $http_host; |
这样:关闭 Host 校验解决「带端口被拒」,$http_host 带端口解决 CSRF,ReverseProxySupportEnabled + 可信列表让 qBittorrent 用 X-Forwarded-For 取到真实客户端 IP。
用 API 设置的方法:先本地登录拿 cookie,再
curl -b cookie -X POST http://127.0.0.1:8080/api/v2/app/setPreferences --data-urlencode 'json={"web_ui_host_header_validation_enabled":false,"web_ui_reverse_proxy_enabled":true,"web_ui_reverse_proxies_list":"127.0.0.1"}'
改完即时生效并写回 conf。
关于双层认证
可以给每个 nginx 站点再加一层 HTTP Basic Auth(auth_basic + htpasswd)。但实测双层认证会让部分客户端(尤其 Jellyfin)无法正常工作,所以这里只用「SSL + 各软件自带登录」。相应地,各软件的登录密码一定要设强。
六、启动顺序与验证
推荐顺序:
- 建
/data目录与权限 - qBittorrent:装、写 conf、
systemctl enable --now qbittorrent-nox@<user>、取临时密码改永久密码、下载路径指/data/media/downloads - Jellyfin:装、把
jellyfin加进<group>、媒体库指/data/media/library(先关硬件加速验证直通) - MoviePilot:填好 compose 的
SUPERUSER_PASSWORD后docker compose up -d,配好下载器/目录,开硬链接整理 - 验证硬链接成立:整理后比对同一片段的 inode 是否相同
1
stat -c %i /data/media/downloads/xxx /data/media/library/xxx # 两个 inode 相同=硬链接成功
- Cloudreve:装、软链数据目录、
systemctl enable --now cloudreve、建管理员(加密保持关闭) - nginx:配好四个反代 + qBittorrent 的额外项,
nginx -t && reload - 收尾:锁版本 + 备份
1 | # 服务状态 |
附:端口一览
| 服务 | 本地监听 | 公网(nginx,示例) |
|---|---|---|
| Jellyfin | 8096 | 44301 |
| qBittorrent | 127.0.0.1:8080 | 44302 |
| MoviePilot | 3000(host 网络) | 44303 |
| Cloudreve | 127.0.0.1:5212 | 44304 |
| BT 入站 | 6881 | —(路由器/防火墙按需放行) |
安全提醒:若机器有公网 IPv6,确认服务没有监听
[::](IPv6 全局可路由);公网只暴露 nginx 的 SSL 端口,各服务用强密码。


