部署配方 · 媒体
Pi 4 上的 Jellyfin:什么格式才不用转码
关于树莓派上的 Jellyfin,最诚实的总结是:它是个称职的文件服务器,是个平庸的转码器。只要客户端自己解得动,一切都很顺;一旦需要树莓派用 CPU 转码,4B 大约只够一路 1080p,再多就开始掉帧。这篇讲的是怎么整理媒体库,让转码几乎不发生。
| 容器镜像 | jellyfin/jellyfin:10.10.7 |
|---|---|
| 宿主端口 | :8096/tcp, :7359/udp |
| 数据目录 | /srv/homelab/jellyfin/config |
| 内存预留 | 900 MB |
| CPU 上限 | 2.0 vCPU (cpus: "2.0") — transcoding is the only heavy job on this rack |
| 更新节奏 | follow the stable branch, roughly every two months; read the release notes for library schema migrations before pulling |
| ARM 兼容 | arm64 native. Hardware H.264 decode works through the V4L2 device; HEVC and AV1 will fall back to software even on a Pi 5 |
| 宿主 | 容器 | proto | 暴露 | 用途 |
|---|---|---|---|---|
| :8096 | 8096 | tcp | 仅局域网 | web UI, API and the apps on the TV and phones |
| :7359 | 7359 | udp | 仅局域网 | client auto-discovery on the local network |
部署步骤
先在宿主机上挂载共享
挂载和验证都在宿主机上做完再碰 Docker。容器带着空的 /media 启动会显示空库,然后把你引到错误的方向去排查。
run sudo mkdir -p /mnt/nas/media sudo mount -a ls /mnt/nas/media | head findmnt /mnt/nas/media -o TARGET,SOURCE,FSTYPE,OPTIONS用你自己的用户建配置和缓存目录
官方镜像里 Jellyfin 以 uid 1000 运行。让 Docker 以 root 建出来的目录,会导致服务能启动却写不了自己的数据库。
run sudo mkdir -p /srv/homelab/jellyfin/{config,cache} sudo chown -R 1000:1000 /srv/homelab/jellyfin启动并看开头几行日志
最该确认的是 V4L2 设备有没有被接受。Docker 拒绝某个设备节点时容器会立刻退出,并给出明确报错。
run cd /srv/homelab/jellyfin && docker compose up -d docker logs --tail 30 jellyfin | grep -i -E 'v4l2|device|startup complete'加媒体库时填容器内路径,不是宿主机路径
Jellyfin 里的库路径是 /media/movies,不是 /mnt/nas/media/movies。填错的话,库能扫描成功,但重启后一个文件都找不到。
run docker exec jellyfin ls /media # 这里打印出来的,才是你在媒体库对话框里该填的路径在请别人用之前,先证明直连播放成立
在每个客户端上播一个确定的 H.264 文件,同时盯着仪表盘。写着 Direct Play 的流对树莓派几乎零成本;原因一栏写软件转码的,才是你要找的目标。
run docker stats --no-stream jellyfin # 播放期间 CPU 低于 15%,说明是直连播放
直接播放就是全部设计目标
树莓派的 VideoCore GPU 只硬解 H.264,其它格式基本帮不上忙。所以媒体库统一成 H.264 + AAC,容器用 MP4 或 MKV,并且所有客户端都设为直接播放。做到这一点,一台 4B 同时推三路流,CPU 占用不到 15%。
HEVC 是最有意思的一种。Pi 5 能硬解,Pi 4 不能,最近三年的手机都能。要不要保留 HEVC 文件,完全取决于你手上哪些客户端比这个编码格式还老。
| 文件编码 | Pi 4B | Pi 5 | 常见手机 | 本机架结论 |
|---|---|---|---|---|
| H.264 1080p | 硬解 | 硬解 | 直接播放 | 保留,这就是目标格式 |
| HEVC 1080p | 软解,掉帧 | 硬解 | 直接播放 | 只在电视能解时保留 |
| HEVC 4K | 不行 | 勉强 | 通常可以 | 别放进库 |
| AV1 | 不行 | 不行 | 仅最新手机 | 不要收 |
媒体文件到底放在哪
媒体放在 NAS 上通过 SMB 共享,在树莓派上挂到 /mnt/nas/media,再以只读方式挂进容器。树莓派存元数据数据库和图片,NAS 存文件。这个分工很关键:树莓派挂了,你损失的是一次重新扫描,不是一整个媒体库。
只读不只是洁癖:它让 Jellyfin 在媒体旁边写 .nfo、下封面的习惯变成无害行为,也让一个配错的库路径不可能删掉任何东西。
# 树莓派上的 /etc/fstab
//192.168.10.5/media /mnt/nas/media cifs credentials=/etc/cifs-media.cred,uid=1000,gid=1000,ro,vers=3.0,nofail,x-systemd.automount 0 0把视频设备交给容器
Raspberry Pi OS 上的硬解走 V4L2 的 mem2mem 设备 /dev/video10 到 /dev/video12。它们在宿主机上存在,在容器里不存在——这就是为什么同一个文件 VLC 播得好好的,Jellyfin 里却把四个核全占满。
把这三个设备透传进去,并让容器加入 video 组。Raspberry Pi OS 上组号是 993,某些 arm64 发行版是 44,所以要在宿主机上确认,别照抄教程里的数字。
getent group video
# raspberry pi os 上是 video:x:993:
ls -l /dev/video1*
# 只透传真实存在的节点;节点不存在会让 docker 直接拒绝启动容器把转码器从用户面前藏起来
即便硬解都配好了,总会有客户端请求服务器软转不了的那个码率。给每个用户设一个和网络匹配的码率上限——千兆有线 12Mbps,平板上 wifi 的 4Mbps——并且强制生效,这样请求就变成了「要么直接播放要么不播」,而不是卡顿的转码。
H.264 的硬件转码值得开启:连续两路 1080p 走 V4L2,比一路纯软转还省 CPU。
docker exec jellyfin ffmpeg -hide_banner -hwaccels | head -5
# 看到 v4l2m2m 才算设备透传生效了compose 文件
整份文件放在 /srv/homelab/jellyfin/compose.yaml。标签固定版本号,不用 latest——树莓派上回滚比升级麻烦得多。
services:
jellyfin:
image: jellyfin/jellyfin:10.10.7
container_name: jellyfin
restart: unless-stopped
ports:
- "8096:8096/tcp"
- "7359:7359/udp"
environment:
TZ: Asia/Shanghai
JELLYFIN_PublishedServerUrl: http://192.168.10.20
volumes:
- /srv/homelab/jellyfin/config:/config
- /srv/homelab/jellyfin/cache:/cache
- /mnt/nas/media:/media:ro
devices:
- /dev/video10:/dev/video10
- /dev/video11:/dev/video11
- /dev/video12:/dev/video12
group_add:
- "993" # video group on Raspberry Pi OS
networks: [rack]
networks:
rack:
external: true加固清单
- 只通过局域网内的反向代理访问,外网访问走 WireGuard,不开公网端口
- 在 Jellyfin 后台关闭远程访问,服务不向家外自我暴露
- 一人一个账号,不共用登录,管理员权限只保留在一个账号上
- V4L2 设备只读透传,容器去掉一切用不到的能力
- 媒体目录以只读方式挂载:服务端本来就不该往媒体库里写东西
备份方案
配置目录每晚同步到 NAS;媒体库本身就在 NAS 上,由 NAS 那边负责备份——树莓派上没有任何不可替代的数据。唯一值得手工导出的是用户列表和观看进度,因为「继续观看」重新累积起来要靠好几周的观看量。
上线后验证
- 电视上播放 1080p H.264,Jellyfin 仪表盘显示 Direct Play
- 两路同时播放时 docker stats 的 CPU 占用低于 20%
- 客户端 App 里自动发现能直接出现条目,不用手输 IP
- 重启容器后不会全库重扫,说明配置卷可写且持久
踩过的坑
- 媒体只读挂载会破坏内置的字幕下载功能——它要把 .srt 写在视频旁边。要么提前下好字幕,要么接受它失败、改用把字幕存到配置目录的选项。
- 不透传 V4L2 设备时 Jellyfin 不会报警,它只是默默软解、吃满四个核,让这台派给人一种「坏了」的感觉,而你多半会先怀疑网络。
- CIFS 挂载没加 x-systemd.automount,容器会在共享挂载完成之前启动,看到空目录,直到下次重启都显示库里有零个条目。
- Jellyfin 会定期写元数据数据库。放在 SD 卡上就是慢性死亡;把 /config 留在固态盘上。
硬件与选型问答
- Jellyfin 用 Pi 4 够吗,还是必须 Pi 5?
- 4GB 的 4B 稳跑两三路 1080p 直连播放。只有当你的库是 HEVC、而客户端又解不动时,才值得买 Pi 5——它的 HEVC 硬解是唯一真正改变这台机器能力的升级。
- Jellyfin 和 Immich 能跑在同一块 8GB 的板子上吗?
- 能,本机架就是这么跑的。给 Jellyfin 算 900MB(两路流),给 Immich 算 1.2GB(五万张照片以内),剩下的留给反代和监控。绝对不能做的是把两者塞进一块 4GB 的板子还指望转码能用。
- 为什么不直接用 NAS 自带的影音套件?
- 如果 NAS 自带并且支持你的客户端,那就用它——省下 900MB 和一个下午。Jellyfin 在这里站得住脚,是因为电视端的客户端更好用,而且观看记录和用户账号属于你,而不属于某个厂商的账号体系。