这是一份可以照着一步步装的手册:在一台 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
2
3
4
5
# 建目录,全部归属 uid 1000
sudo mkdir -p /data/config/moviepilot /data/config/cloudreve \
/data/media/downloads /data/media/library /data/media/.transcode /data/cloud
sudo chown -R 1000:1000 /data
chmod g+rx /data /data/media /data/media/library /data/media/downloads

目录结构:

1
2
3
4
5
6
7
/data
├── config/{moviepilot,cloudreve} # 两个服务的配置/数据
├── media/
│ ├── downloads/ # qBittorrent 下载
│ ├── library/ # MoviePilot 硬链接整理后 ← Jellyfin 指向这里
│ └── .transcode/ # Jellyfin 转码临时目录
└── cloud/ # Cloudreve 独立网盘存储(可选)

装完 Jellyfin 后,把它的系统用户加进 <group>,让它能读 /data(详见下文 Jellyfin 部分)。


一、qBittorrent(下载器)

安装

Debian 源里就有,qbittorrent-nox 是无 GUI 的服务器版:

1
2
3
sudo apt update
sudo apt install -y qbittorrent-nox
apt-cache policy qbittorrent-nox # 确认版本

启动(用包自带的模板 unit)

包内自带 qbittorrent-nox@.service 模板,User=%i 会以「实例名」这个用户身份运行。用你的登录用户名做实例名,UID 就天然一致:

1
sudo systemctl enable --now qbittorrent-nox@<user>.service

配置文件

配置在 ~/.config/qBittorrent/qBittorrent.conf(即运行用户的家目录)。建议在首次启动前就写好,这样它一开始就只监听本地、下载路径也对。关键项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[BitTorrent]
Session\DefaultSavePath=/data/media/downloads ; 下载目录
Session\Port=6881 ; BT 入站端口(固定,别用随机)
Session\DHTEnabled=true ; 启用 DHT(仅对非私有种子生效)
Session\AnonymousModeEnabled=false ; PT 必须关匿名模式,否则不暴露 peer 身份
Session\QueueingSystemEnabled=true
Session\MaxActiveDownloads=8

[LegalNotice]
Accepted=true ; 接受声明,免去首次交互

[Preferences]
WebUI\Address=127.0.0.1 ; 只监听本地,公网交给 nginx
WebUI\Port=8080
WebUI\Username=admin
WebUI\LocalHostAuth=true
Downloads\SavePath=/data/media/downloads
Downloads\TempPathEnabled=false ; 关闭“未完成放子目录”,保持单一根目录

改完 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
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo mkdir -p /etc/apt/keyrings
# 下载并导入签名key(注意 keyring 要 644,否则 apt 沙箱用户读不了)
curl -fsSL https://repo.jellyfin.org/debian/jellyfin_team.gpg.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/jellyfin.gpg
sudo chmod 644 /etc/apt/keyrings/jellyfin.gpg

# <codename> 换成你的发行版代号,如 trixie / bookworm
echo "deb [signed-by=/etc/apt/keyrings/jellyfin.gpg] https://repo.jellyfin.org/debian <codename> main" \
| sudo tee /etc/apt/sources.list.d/jellyfin.list
sudo chmod 644 /etc/apt/sources.list.d/jellyfin.list

sudo apt update
sudo apt install -y jellyfin jellyfin-ffmpeg8
sudo systemctl enable --now jellyfin

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
2
3
sudo usermod -aG <group> jellyfin     # 注意是“组名”,不是数字 GID
sudo systemctl restart jellyfin
# 验证:sudo -u jellyfin sh -c '[ -r /data/media/library ] && echo OK'

若要用硬件解码设备(/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
2
sudo chown jellyfin:<group> /data/media/.transcode
sudo chmod 2775 /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
2
3
4
5
6
7
sudo mkdir -p /opt/cloudreve && cd /opt/cloudreve
# <版本> 到 https://github.com/cloudreve/cloudreve/releases 查最新 tag
sudo curl -L -o cloudreve.tar.gz \
https://github.com/cloudreve/cloudreve/releases/download/<版本>/cloudreve_<版本>_linux_arm64.tar.gz
sudo tar xzf cloudreve.tar.gz cloudreve
sudo chmod +x cloudreve
sudo rm cloudreve.tar.gz

配置文件 conf.ini

放到 /data/config/cloudreve/conf.ini:

1
2
3
4
5
[System]
Mode = master
Listen = 127.0.0.1:5212 ; 只监听本地,公网交给 nginx
SessionSecret = <随机串> ; 生成:openssl rand -hex 32
HashIDSalt = <随机串> ; 生成:openssl rand -hex 16

数据目录: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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=Cloudreve
After=network-online.target
Wants=network-online.target

[Service]
WorkingDirectory=/opt/cloudreve
ExecStart=/opt/cloudreve/cloudreve server -c /data/config/cloudreve/conf.ini
Restart=on-failure
User=1000
Group=1000
UMask=002

[Install]
WantedBy=multi-user.target
1
2
sudo systemctl daemon-reload
sudo systemctl enable --now cloudreve

启动子命令是 cloudreve server(不是 -d),-c 指定 conf 路径,默认端口 5212。

首次建管理员

v4 不打印默认口令。打开 Web 走首次安装页创建管理员——注册的第一个账号自动进入 Admin 组,所以服务起来后第一时间把管理员建掉。加密功能默认关闭,建议保持关闭(开启后媒体文件对 Jellyfin/MP 变不可读,且 master key 丢了数据无法恢复)。


四、MoviePilot(Docker)

装 Docker

1
2
3
sudo apt install -y docker.io docker-compose
sudo systemctl enable --now docker
sudo usermod -aG docker <user> # 需重新登录生效

docker-compose.yml

放到 /data/config/moviepilot/docker-compose.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
services:
moviepilot:
image: jxxghp/moviepilot-v2:latest # 官方镜像,amd64/arm64 都有
container_name: moviepilot
restart: unless-stopped # 除手动停止外,异常/重启都自动拉起
network_mode: host # 用宿主网络:容器内可直接访问宿主的本地代理、便于站点直连
environment:
- PUID=1000 # 容器内写文件的 uid,必须=/data 属主,否则硬链接产物权限错
- PGID=1000 # 同上,gid
- UMASK=002 # 新建文件权限掩码,002=组可写,便于协作
- TZ=Asia/Shanghai # 时区
- SUPERUSER=admin # 后台管理员用户名
- SUPERUSER_PASSWORD=<强密码> # 管理员密码,务必改强
- MOVIEPILOT_AUTO_UPDATE=false # 关容器内自更新,升级改手动 docker compose pull
- PROXY_HOST=http://127.0.0.1:7890 # 走本地代理访问 TMDB 等;无代理则删掉此行
volumes:
- /data/config/moviepilot:/config # MP 自身配置
# 下面三行是关键:容器内外必须“同路径”,MP 才能按同一路径做硬链接
- /data/media/downloads:/data/media/downloads # 下载目录(原路径映射,不要用别名)
- /data/media/library:/data/media/library # 媒体库(原路径映射)
- /data:/data # 整个 /data 也映射进去
ports: [] # host 网络下 ports 无效,留空即可

逐项要点:

  • 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
2
3
cd /data/config/moviepilot
sudo docker compose up -d
sudo docker compose logs -f 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
2
3
4
map $http_upgrade $nas_connection_upgrade {
default upgrade;
'' close;
}

通用 server 块模板

以 Jellyfin 为例,新建 /etc/nginx/sites-available/nas-jellyfin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server {
listen 44301 ssl;
listen [::]:44301 ssl;
server_name nas.example.com;

ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;

client_max_body_size 0; # 0=不限上传大小(Cloudreve 传大文件必须)

location / {
proxy_pass http://127.0.0.1:8096; # 后端服务地址
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr; # 单级反代用 $remote_addr,防客户端伪造
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Upgrade $http_upgrade; # WebSocket 升级
proxy_set_header Connection $nas_connection_upgrade;
proxy_buffering off; # SSE/WebSocket 需关缓冲
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}

启用:

1
2
sudo ln -s /etc/nginx/sites-available/nas-jellyfin /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

其余三个照抄,只改端口和 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
2
3
4
WebUI\HostHeaderValidation=false          ; 关掉 Host 头校验(服务只绑本地,风险可控)
WebUI\ReverseProxySupportEnabled=true ; 开启反向代理支持
WebUI\TrustedReverseProxiesList=127.0.0.1 ; 只信任 nginx 所在 IP
WebUI\CSRFProtection=true ; CSRF 保持开启

同时 nginx 那份 qBittorrent 配置里,把 Host 和 X-Forwarded-Host 都改成带端口的 $http_host(让 CSRF 的 Origin 比对通过):

1
2
proxy_set_header Host             $http_host;
proxy_set_header X-Forwarded-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 + 各软件自带登录」。相应地,各软件的登录密码一定要设强。


六、启动顺序与验证

推荐顺序:

  1. 建 /data 目录与权限
  2. qBittorrent:装、写 conf、systemctl enable --now qbittorrent-nox@<user>、取临时密码改永久密码、下载路径指 /data/media/downloads
  3. Jellyfin:装、把 jellyfin 加进 <group>、媒体库指 /data/media/library(先关硬件加速验证直通)
  4. MoviePilot:填好 compose 的 SUPERUSER_PASSWORD 后 docker compose up -d,配好下载器/目录,开硬链接整理
  5. 验证硬链接成立:整理后比对同一片段的 inode 是否相同
    1
    stat -c %i /data/media/downloads/xxx /data/media/library/xxx   # 两个 inode 相同=硬链接成功
  6. Cloudreve:装、软链数据目录、systemctl enable --now cloudreve、建管理员(加密保持关闭)
  7. nginx:配好四个反代 + qBittorrent 的额外项,nginx -t && reload
  8. 收尾:锁版本 + 备份
1
2
3
4
5
6
7
# 服务状态
systemctl is-active docker jellyfin cloudreve 'qbittorrent-nox@<user>' nginx
# 锁版本,避免 apt 升级把验证过的组件换掉
sudo apt-mark hold jellyfin jellyfin-ffmpeg8 qbittorrent-nox
# 备份(全是目录,tar 即可)
tar czf nasconf-$(date +%F).tar.gz /data/config ~/.config/qBittorrent /var/lib/jellyfin /etc/jellyfin \
/etc/systemd/system/cloudreve.service /etc/nginx/sites-available/nas-* /etc/nginx/conf.d/nas-upgrade-map.conf

附:端口一览

服务 本地监听 公网(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 端口,各服务用强密码。