Jellyfin 是一个开源媒体服务器,可以用来管理电影、电视剧、音乐和照片,并通过浏览器、电视盒子、手机 App 访问。

本文记录在 QNAP NAS 上用 Docker 部署 Jellyfin 的方式。部署方式优先使用 Docker Compose,Container Station 用来查看容器状态和日志。

官方文档入口:

版本说明

本文固定使用 Jellyfin:

jellyfin/jellyfin:10.11.11

不要直接使用 jellyfin/jellyfin:latest。Jellyfin 官方说明 latest 会跟随最新稳定版,包括大版本和小版本变化。家庭媒体库长期运行时,建议固定到具体版本,升级前先备份配置。

官方容器镜像常见标签含义:

latest     最新稳定版,浮动
10         最新 10.Y.Z,浮动
10.11      最新 10.11.Z,浮动
10.11.11   固定具体版本

部署规划

本文假设 QNAP NAS 的 IP 是:

192.168.3.200

Jellyfin 访问地址:

http://192.168.3.200:8096

目录规划:

/share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose      放 compose.yaml
/share/CACHEDEV2_DATA/DockerDatas/jellyfin/config       放 Jellyfin 配置
/share/CACHEDEV2_DATA/DockerDatas/jellyfin/cache        放 Jellyfin 缓存和转码临时文件
/share/CACHEDEV2_DATA/Media                             放媒体文件

如果你的 QNAP 默认存储池不是 CACHEDEV2_DATA,需要按实际路径修改。可以通过下面命令查看默认数据卷:

getcfg SHARE_DEF defVolMP -f /etc/config/def_share.info

前置条件

QNAP 需要先安装:

Container Station
Docker Engine
Docker Compose

在 QNAP Web 管理页面打开:

App Center -> 搜索 Container Station -> 安装

然后开启 SSH:

控制台与管理工具 -> Telnet / SSH -> 允许 SSH 连接

从电脑登录 NAS:

ssh admin@192.168.3.200

检查 Docker:

docker version
docker compose version

如果 SSH 中找不到 docker,先查找 Container Station 自带的 Docker 路径:

find /share -name docker -type f 2>/dev/null | grep -Ei 'container.*station|/\.qpkg/'

常见路径类似:

/share/CACHEDEV2_DATA/.qpkg/container-station/bin/docker

临时加入 PATH

export PATH=/share/CACHEDEV2_DATA/.qpkg/container-station/bin:$PATH

如果路径不是 CACHEDEV2_DATA,按实际查到的路径修改。

创建目录

创建 Jellyfin 部署目录:

export NAS_DATA_ROOT=/share/CACHEDEV2_DATA

mkdir -p ${NAS_DATA_ROOT}/DockerDatas/jellyfin/compose
mkdir -p ${NAS_DATA_ROOT}/DockerDatas/jellyfin/config
mkdir -p ${NAS_DATA_ROOT}/DockerDatas/jellyfin/cache
mkdir -p ${NAS_DATA_ROOT}/Media

把电影、电视剧、音乐等文件放到 ${NAS_DATA_ROOT}/Media 下,例如:

/share/CACHEDEV2_DATA/Media/Movies
/share/CACHEDEV2_DATA/Media/TV
/share/CACHEDEV2_DATA/Media/Music

如果你的媒体文件已经在其他共享目录,例如 /share/CACHEDEV2_DATA/Multimedia,后面的 Compose 文件里把媒体路径改成实际目录即可。

Docker Compose 部署

进入部署目录:

cd ${NAS_DATA_ROOT}/DockerDatas/jellyfin/compose

创建 compose.yaml

cat > compose.yaml <<'EOF'
services:
  jellyfin:
    image: jellyfin/jellyfin:10.11.11
    container_name: jellyfin
    restart: unless-stopped
    ports:
      - "8096:8096/tcp"
      - "7359:7359/udp"
    environment:
      TZ: Asia/Shanghai
      JELLYFIN_PublishedServerUrl: http://192.168.3.200:8096
    volumes:
      - /share/CACHEDEV2_DATA/DockerDatas/jellyfin/config:/config
      - /share/CACHEDEV2_DATA/DockerDatas/jellyfin/cache:/cache
      - type: bind
        source: /share/CACHEDEV2_DATA/Media
        target: /media
        read_only: false
EOF

这里把媒体目录挂载成只读,Jellyfin 可以扫描和播放,但不能修改媒体文件。这样更适合 NAS 上已经整理好的媒体库。

如果要让 Jellyfin 下载字幕、写入 NFO 或直接管理媒体文件,可以把 read_only: true 改成:

read_only: false

启动:

docker compose up -d

查看状态:

docker compose ps

查看日志:

docker compose logs -f

docker run 部署

如果只是临时测试,也可以直接使用 docker run

docker run -d \
  --name jellyfin \
  --restart unless-stopped \
  -p 8096:8096/tcp \
  -p 7359:7359/udp \
  -e TZ=Asia/Shanghai \
  -e JELLYFIN_PublishedServerUrl=http://192.168.3.200:8096 \
  -v /share/CACHEDEV2_DATA/DockerDatas/jellyfin/config:/config \
  -v /share/CACHEDEV2_DATA/DockerDatas/jellyfin/cache:/cache \
  --mount type=bind,source=/share/CACHEDEV2_DATA/Media,target=/media,readonly \
  jellyfin/jellyfin:10.11.11

长期运行更推荐 Compose,后续升级、备份和迁移都更清楚。

访问和初始化

浏览器访问:

http://192.168.3.200:8096

第一次打开会进入 Jellyfin 初始化向导:

1. 选择语言
2. 创建管理员用户
3. 添加媒体库
4. 配置远程访问
5. 完成初始化

添加媒体库时,容器内路径使用:

/media/Movies
/media/TV
/media/Music

如果页面打不开,先检查:

docker compose ps
docker compose logs -f
curl -I http://127.0.0.1:8096
curl -I http://192.168.3.200:8096

Container Station 中查看

通过 SSH 使用 Docker Compose 启动后,Jellyfin 容器通常也会出现在 Container Station 的容器列表里。

可以在 Container Station 中查看:

容器状态
CPU / 内存占用
端口映射
容器日志

但建议不要在 Container Station 里随手修改容器配置。长期维护以 compose.yaml 为准,避免 GUI 配置和文件配置不一致。

硬件转码

如果 QNAP 是 Intel 或 AMD x86_64 机型,可能可以使用 /dev/dri 做硬件转码。先检查设备是否存在:

ls -l /dev/dri

如果能看到类似:

card0
renderD128

再查看 renderD128 的组 ID:

stat -c '%n %a %U %G %g' /dev/dri/renderD128

示例输出:

/dev/dri/renderD128 660 root render 107

最后一列 107 就是渲染设备的 GID。为了让容器内的 Jellyfin 进程能访问这个设备,可以在 Compose 目录里创建 .env

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose
echo "RENDER_GID=$(stat -c '%g' /dev/dri/renderD128)" > .env
cat .env

然后在 compose.yaml 中给 Jellyfin 增加 devicesgroup_add

services:
  jellyfin:
    image: jellyfin/jellyfin:10.11.11
    container_name: jellyfin
    restart: unless-stopped
    devices:
      - /dev/dri:/dev/dri
    group_add:
      - "${RENDER_GID}"
    ports:
      - "8096:8096/tcp"
      - "7359:7359/udp"
    environment:
      TZ: Asia/Shanghai
      JELLYFIN_PublishedServerUrl: http://192.168.3.200:8096
    volumes:
      - /share/CACHEDEV2_DATA/DockerDatas/jellyfin/config:/config
      - /share/CACHEDEV2_DATA/DockerDatas/jellyfin/cache:/cache
      - type: bind
        source: /share/CACHEDEV2_DATA/Media
        target: /media
        read_only: true
    networks:
      - homestore
networks:
  homestore:
    driver: bridge
    name: homestore    

修改后重启:

docker compose down
docker compose up -d

确认容器里能看到设备:

docker exec -it jellyfin ls -l /dev/dri

然后在 Jellyfin Web 页面中打开:

控制台 -> 播放 -> 转码 -> 硬件加速

Intel 核显一般选择:

Intel Quick Sync

例如 QNAP 使用 Intel Celeron N5095 这类 Intel 核显机型时,优先选择:

Intel Quicksync (QSV)

如果 Quick Sync 不正常,可以退一步测试:

VAAPI

常用设置建议:

硬件加速: Intel Quick Sync
硬件解码: 先勾 H.264、HEVC、MPEG2、VC1、VP9
硬件编码: 开启
允许硬件色调映射: 先关闭,确认普通转码正常后再测试
转码临时路径: 保持默认或使用 /cache

老款 Intel 核显不一定支持 AV1、HEVC 10-bit、HDR 色调映射等能力。不要一次把所有选项都勾上,先让 1080p H.264 / HEVC 转码跑通,再逐项增加。

测试方式:

1. 手机播放一个高码率视频
2. 把质量手动调低,例如 1080p - 8 Mbps
3. 打开播放信息
4. 确认播放方式从 Direct 变成 Transcode
5. 观察 NAS CPU 是否明显低于纯软件转码

同时查看 Jellyfin 日志:

docker compose logs -f

如果硬件转码正常,日志里的 ffmpeg 命令通常会出现和 QSV 或 VAAPI 相关的参数。

注意,修改 compose.yaml 只代表容器能看到 /dev/dri,不代表 Jellyfin 已经启用硬件转码。可以检查配置文件:

grep -n 'HardwareAccelerationType' \
  /share/CACHEDEV2_DATA/DockerDatas/jellyfin/config/config/encoding.xml

如果输出是:

<HardwareAccelerationType>none</HardwareAccelerationType>

说明 Jellyfin 后台仍然没有开启硬件加速。需要在 Web 页面里选择 Intel Quick SyncVAAPI 并保存。

也可以通过日志判断当前是不是硬件转码:

docker compose logs -f

如果 ffmpeg 命令里看到:

-codec:v:0 libx264

说明当前还是 CPU 软件转码。如果硬件转码生效,通常会看到:

qsv
vaapi

例如看到下面这些参数,就说明 QSV 已经生效:

-init_hw_device qsv
-codec:v:0 h264_qsv
scale_vaapi
format=qsv

如果 QSV 已经生效但 iPhone 仍然卡,问题就不再是“硬件转码没打开”,而要继续看客户端和播放链路:

播放信息是否仍然是 Direct
是否通过 Safari Web 页面播放
是否走了 HTTPS 反向代理
是否开启了复杂字幕
是否使用了过低码率或频繁拖动进度条

建议优先测试这几种组合:

1. iPhone 直连 http://192.168.3.200:8096
2. 固定质量 1080p 8 Mbps 或 720p 4 Mbps
3. 关闭字幕
4. 使用 Jellyfin App 或外部播放器 VLC
5. 确认播放信息显示 Transcode,而不是 Direct

是否能用硬件转码取决于 NAS CPU、QNAP 系统、驱动、设备权限和视频编码格式。不要一开始就依赖硬解,先确认普通播放正常,再开启硬解测试。

如果开启硬件转码后无法播放,先回到 Jellyfin 后台关闭硬件加速,再查看日志:

docker compose logs -f

手机播放卡顿排查

如果电脑播放正常,但手机播放视频卡顿,先打开手机播放器里的播放信息。看到类似:

质量: 自动 - Direct
播放速度: 1x

这里的 Direct 很关键。它表示 Jellyfin 正在让手机直接播放原始视频文件,NAS 基本没有参与转码。此时卡顿通常不是 Jellyfin 容器本身的问题,而是下面几类原因:

手机 Wi-Fi 信号弱,尤其是 2.4G 网络
原片码率太高,例如 4K、蓝光 Remux、大体积 HEVC
手机硬件或当前播放器对视频编码支持不好
手机实际走了公网、反向代理或弱网络链路
字幕、音轨、封装格式导致手机端播放压力变大

硬件转码只对 Transcode 生效。只要播放信息仍然显示 Direct,即使已经配置好 /dev/dri,NAS 的硬件转码也不会参与这次播放。

先确认手机和电脑是不是同一条访问路径。为了排除反向代理和公网链路,手机先连同一个局域网 Wi-Fi,并直接访问:

http://192.168.3.200:8096

不要一开始就用:

https://jellyfin.jihw.top

如果局域网直连不卡,而域名访问卡,问题多半在 Nginx Proxy Manager、路由器、公网回流、DNS 或外网带宽上。

再看原片码率。播放时打开“播放信息”,重点看:

视频编码: H.264 / HEVC / AV1
分辨率:   1080p / 4K
码率:     多少 Mbps
播放方式: Direct / Direct Stream / Transcode

如果是 Direct,并且原片是高码率 4K 或 HEVC,手机卡顿很常见。可以在手机播放界面把质量从“自动”改成固定码率,例如:

1080p - 10 Mbps
720p - 4 Mbps

这样会强制 Jellyfin 转码,降低手机端和网络压力。转码后再观察:

1. 手机是否不卡了
2. NAS CPU 是否飙高
3. Jellyfin 日志里是否出现 ffmpeg 转码错误

查看日志:

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose
docker compose logs -f

如果强制降低质量后不卡,说明主要问题是原片码率、手机解码能力或 Wi-Fi 带宽。后续可以考虑开启硬件转码。

这种情况可以直接按下面思路处理:

局域网手机播放 1080p: 先试 8~12 Mbps
公网手机播放 1080p: 先试 4~8 Mbps
移动网络播放:       先试 2~4 Mbps
4K 原盘或 Remux:    不建议手机 Direct,优先转码或准备 1080p 版本

如果只是偶尔手机看,可以在客户端播放时手动降低质量。如果经常在手机上看,建议开启硬件转码,或者为高码率影片额外准备一份 1080p / H.264 / 8Mbps 左右的版本。

实际测试时可以按手机稳定性重新定档。如果 1.5 Mbps 稳定,而 3 Mbps 还需要观察,就先把手机端固定在:

720p 1.5 Mbps

然后再逐步测试:

720p 3 Mbps
1080p 4 Mbps
1080p 8 Mbps

哪一档连续播放 10~15 分钟不卡,就先用哪一档。不要只看能否起播,手机端真正的问题往往出现在几分钟后的 HLS 分片获取和缓冲。

如果降低质量后更卡,说明 NAS 正在软件转码但性能不够。此时要么开启 /dev/dri 硬件转码,要么准备适合手机播放的低码率版本。

还有一个实用办法是更换手机端播放器。Jellyfin 手机 App 里可以尝试:

使用集成播放器
使用外部播放器,例如 VLC
关闭或更换复杂字幕

ASS/SSA 特效字幕、PGS 图形字幕在某些客户端上会触发额外处理,低性能手机上容易卡。测试时可以先关闭字幕,确认是不是字幕导致。

iPhone 自动模式播放一段时间后卡住

如果 iPhone 在“自动”质量下播放到 1 分钟左右卡住,但手动降低码率后正常,通常可以这样判断:

自动模式:  iPhone 直接播放原片,显示 Direct
降低码率:  Jellyfin 转成 HLS 分段流,显示 Transcode

这说明问题主要出在 iPhone 直播放这条链路上,可能是原片封装、60fps、音视频轨道、关键帧、Safari/Web 播放器兼容性或网络缓冲策略导致的。降低码率后变成 HLS 转码流,客户端压力和兼容性问题都会小很多。

可以在 NAS 上看 Jellyfin 日志确认是否转码:

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose
docker compose logs -f

如果日志里的 ffmpeg 命令包含:

-codec:v:0 libx264

说明当前是 CPU 软件转码。开启硬件转码后,日志里通常会出现 qsvvaapi 相关参数。

处理建议:

1. iPhone 客户端不要长期使用自动质量,先固定 1080p 8~12 Mbps
2. 使用 Jellyfin App 时尝试切换集成播放器或外部播放器
3. 关闭复杂字幕测试
4. 开启 QNAP 的 /dev/dri 硬件转码
5. 对特别不兼容的视频,额外准备一份 H.264 / AAC / 1080p 版本

电脑和平板不卡,手机卡

如果同一个视频在电脑和平板上不卡,但华为手机和 iPhone 都卡,基本可以先排除 NAS 磁盘、Jellyfin 容器和整体网络吞吐问题。排查重点放到手机端:

手机是否走浏览器播放,而不是 Jellyfin App
手机是否连到 2.4G Wi-Fi 或弱信号区域
手机是否启用了省电模式、低数据模式、私有地址、代理或 VPN
手机播放信息是否在 Direct / Transcode 之间变化
是否通过 HTTPS 反向代理访问,而电脑和平板用的是内网直连

建议固定一个测试矩阵,不要同时改多个变量:

电脑:  http://192.168.3.200:8096  自动质量
平板:  http://192.168.3.200:8096  自动质量
手机:  http://192.168.3.200:8096  1080p 8 Mbps
手机:  http://192.168.3.200:8096  720p 4 Mbps
手机:  https://jellyfin.jihw.top   1080p 8 Mbps

如果手机直连内网不卡、域名访问卡,问题在反向代理、DNS、路由器回流或 HTTPS 链路。

如果手机直连内网也卡,但电脑和平板不卡,优先处理手机客户端:

1. 不用 Safari / 手机浏览器,改用 Jellyfin App
2. Jellyfin App 中尝试切换集成播放器或外部播放器
3. 外部播放器优先测试 VLC
4. 关闭字幕
5. 固定质量,不使用自动

QSV 生效后,日志中会出现:

-init_hw_device qsv
-codec:v:0 h264_qsv

如果日志已经是 h264_qsv,但手机仍然卡,就不要继续纠结“硬件转码有没有打开”。下一步应该固定手机质量、换播放器、绕过反向代理测试。

还有一种容易误判的情况:Jellyfin 后台已经开启 QSV,但手机客户端在“自动”质量下仍然选择 Direct。这时日志里只会看到 MediaInfoHelper,不会出现新的 ffmpeg 命令。也就是说硬件转码虽然已经配置好了,但这次播放根本没有用上。

判断方法很简单:播放时打开手机端“播放信息”:

Direct      没有用转码,QSV 不参与
Transcode   正在转码,才会用到 QSV/VAAPI

如果手机端仍然显示 Direct,就把质量固定到低于原片的值,例如:

1080p 8 Mbps
720p 4 Mbps

如果固定质量后日志仍然没有 ffmpeg 命令,说明客户端没有真正触发转码,继续降低质量或换播放器测试。

如果固定质量后日志出现 h264_qsv,但仍然在 1~2 分钟后卡住,优先怀疑这个视频文件本身的封装、时间戳、关键帧或手机端播放器兼容性。可以额外准备一份手机友好的版本:

H.264
AAC
1080p 或 720p
30fps
MP4
faststart

如果日志里反复出现:

Current HLS implementation doesn't support non-keyframe breaks but one is requested
Stopping ffmpeg process with q command

说明手机端在请求 HLS 分片或跳转位置时,服务端需要不断停掉当前转码任务并从新的时间点重新生成分片。这类问题通常不是 NAS 性能不足,而是手机播放器、HLS 分片、源文件关键帧间隔或封装兼容性叠在一起导致的。

实测如果 1.5 Mbps 稳定,而 3 Mbps 仍然在 2~3 分钟附近卡住,就先把手机端固定在稳定档位:

720p 1.5 Mbps

然后再考虑长期方案:

1. 手机端使用外部播放器 VLC / Infuse
2. 避免使用自动质量
3. 关闭字幕测试
4. 给问题视频转一份手机友好版本
5. 用 30fps、固定关键帧间隔、H.264、AAC、MP4 faststart

DLNA 和局域网发现

本文默认使用桥接网络,并映射了:

8096/tcp  Web 页面和客户端访问
7359/udp  局域网发现

Jellyfin 官方说明 Docker 默认是 bridge 网络,如果要使用 DLNA,通常需要 host 网络。QNAP 上使用 host 网络可能和 NAS 自身服务端口冲突,建议先不用 DLNA,优先通过浏览器、手机 App、电视客户端手动填写服务器地址:

http://192.168.3.200:8096

Nginx Proxy Manager 反向代理

如果要通过域名访问,例如:

https://jellyfin.jihw.top

可以用 Nginx Proxy Manager 反代到 Jellyfin:

Domain Names:          jellyfin.jihw.top
Scheme:                http
Forward Hostname/IP:   192.168.3.200
Forward Port:          8096
SSL:                   Let's Encrypt
Force SSL:             开启
Websockets Support:    开启

同时把 compose.yaml 里的 JELLYFIN_PublishedServerUrl 改成最终访问地址:

environment:
  TZ: Asia/Shanghai
  JELLYFIN_PublishedServerUrl: https://jellyfin.jihw.top

修改后重启:

docker compose down
docker compose up -d

不要把没有 HTTPS 的 Jellyfin 直接暴露到公网。公网访问建议走 HTTPS、强密码、反向代理、VPN 或内网穿透访问控制。

常用维护命令

进入部署目录:

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose

停止:

docker compose stop

启动:

docker compose start

重启:

docker compose restart

查看状态:

docker compose ps

查看日志:

docker compose logs -f

备份和恢复

Jellyfin 最重要的是配置目录:

/share/CACHEDEV2_DATA/DockerDatas/jellyfin/config

缓存目录可以不备份:

/share/CACHEDEV2_DATA/DockerDatas/jellyfin/cache

停机备份:

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose
docker compose stop

tar czf /share/CACHEDEV2_DATA/DockerDatas/jellyfin-backup-$(date +%F).tgz \
  /share/CACHEDEV2_DATA/DockerDatas/jellyfin/config \
  /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose/compose.yaml

docker compose start

媒体文件目录一般体积很大,建议通过 QNAP 自己的快照、HBS、外置硬盘或异地备份单独处理。

恢复时保持目录路径一致,然后启动:

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose
docker compose up -d

升级

升级前先备份 config 目录。然后修改 compose.yaml 中的镜像版本,例如从:

image: jellyfin/jellyfin:10.11.11

改成目标版本。

拉取并重建:

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose
docker compose pull
docker compose up -d

查看日志确认启动正常:

docker compose logs -f

不要跨多个大版本直接升级。升级前先看 Jellyfin Releases,确认目标版本是否有数据库迁移、插件兼容性或安全修复说明。

卸载

只删除容器,保留配置和媒体文件:

cd /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose
docker compose down

确认不再需要 Jellyfin 后,再删除配置和缓存:

rm -rf /share/CACHEDEV2_DATA/DockerDatas/jellyfin/config
rm -rf /share/CACHEDEV2_DATA/DockerDatas/jellyfin/cache
rm -rf /share/CACHEDEV2_DATA/DockerDatas/jellyfin/compose

不要误删媒体目录:

/share/CACHEDEV2_DATA/Media

小结

QNAP NAS 上部署 Jellyfin 最稳的方式是:Container Station 提供 Docker 环境,实际部署用 Docker Compose 管理。

普通使用只需要 8096/tcp,局域网发现可以加 7359/udp。硬件转码需要确认 /dev/dri 是否存在,并且要单独测试。公网访问不要直接暴露 HTTP,建议通过 Nginx Proxy Manager 配 HTTPS 反向代理。