Jellyfin 12.4 Docker Compose 部署指南
在一台 Ubuntu 服务器上用 Docker Compose 从零部署 Jellyfin 12.4:镜像选型与网络渠道、编排启动、初始化向导、媒体库创建、HLS 转码链路实测验证,并沉淀向导 404、建库 400、TMDB 不可达、VAAPI 回落等实测排障案例。
Jellyfin 12.4 Docker Compose 部署指南
目标:在一台 Ubuntu 服务器上用 Docker Compose 从零部署 Jellyfin 12.4 全媒体服务器:镜像拉取、编排启动、初始化向导、媒体库创建、转码能力验证与实测排障沉淀,全部命令与输出均在 Ubuntu 24.04 服务器实测逐行照抄。
一、环境信息
本文所有命令与回显来自以下实测环境:
- 操作系统:Ubuntu 24.04.5 LTS(x86_64)
- Docker 版本:
Docker version 29.9.2, build a82e528 - Docker Compose 版本:
Docker Compose version v5.6.1 - Jellyfin 镜像:
jellyfin/jellyfin:12.4(2.49GB,镜像 IDfea863d0cd9e) - 服务器版本实测:
Jellyfin.Server 12.4.0.0(容器内二进制--version回显) - 端口规划:宿主
8146→ 容器8096(HTTP)、宿主8147→ 容器8920(HTTPS) - 目录规划:
/opt/jellyfin/{config,cache,media}(配置/缓存/媒体均落宿主磁盘)
二、镜像选型说明
2.1 官方镜像与标签选择
Jellyfin 官方维护 Docker Hub 镜像 jellyfin/jellyfin,本文选用明确版本标签 12.4 而非 latest:固定标签保证可复现与可回滚(升级时只需改标签号)。镜像自带定制构建的 jellyfin-ffmpeg 与完整 Web UI,无需额外安装播放组件。
⚠️ 注意:
latest标签会随官方发版漂移,生产环境建议始终锁定具体版本号(如12.4),升级走「改标签 + 重新 up」的受控流程(见 9.2 节)。
2.2 镜像内布局
镜像内关键路径(docker exec 实查):
| 路径 | 用途 |
|---|---|
/jellyfin/jellyfin | 服务端主程序(ELF 可执行文件) |
/jellyfin/jellyfin-web | Web UI 静态资源(环境变量 JELLYFIN_WEB_DIR 指向此处) |
/usr/lib/jellyfin-ffmpeg/ffmpeg | 定制构建的 ffmpeg(转码引擎) |
/config | 数据根目录(配置、数据库、日志、元数据、插件都落这里) |
/config/config | XML 配置文件目录(JELLYFIN_CONFIG_DIR) |
/config/log | 服务日志目录(JELLYFIN_LOG_DIR) |
/cache | 转码缓存目录(HLS 分片写入 /cache/transcodes/) |
2.3 网络与拉取渠道
镜像来自 Docker Hub 官方源 docker.io/jellyfin/jellyfin。本机 Docker 守护进程已配置 HTTP 代理 http://192.168.1.62:7897(/etc/systemd/system/docker.service.d/ 下 drop-in 注入 HTTP_PROXY/HTTPS_PROXY 后 systemctl daemon-reload && systemctl restart docker),docker pull 直连代理拉取成功。网络受限环境请为守护进程配置同样的代理,或用 docker save/docker load 离线导入。
三、前置条件
3.1 Docker 与 Compose 就绪检查
docker --version # 确认 Docker 已安装
实测回显:
Docker version 29.9.2, build a82e528
docker compose version # 确认 Compose 插件已安装
实测回显:
Docker Compose version v5.6.1
两个命令任一报 command not found 说明未安装,先按 Docker 官方文档安装 docker-ce 与 compose 插件再继续。
3.2 目录规划
mkdir -p /opt/jellyfin/config /opt/jellyfin/cache /opt/jellyfin/media
无回显即成功。三个目录分别是配置/数据(config)、转码缓存(cache)、媒体库(media)的宿主挂载点,后续 compose 文件直接引用。
ls -d /opt/jellyfin/*
实测回显:
/opt/jellyfin/cache /opt/jellyfin/config /opt/jellyfin/media
3.3 准备测试媒体(ffmpeg 自造素材)
媒体库扫描需要真实媒体文件。没有现成库存时,用 ffmpeg 生成三部测试影片与三首测试音乐(本文实测即用这批素材,注意这是测试素材,非真实影片):
# 电影 1:H.264 640x360 30 秒 + 440Hz 正弦音
ffmpeg -hide_banner -loglevel error -y \
-f lavfi -i "testsrc2=size=640x360:rate=24" -f lavfi -i "sine=frequency=440:duration=30" \
-t 30 -c:v libx264 -preset veryfast -crf 28 -pix_fmt yuv420p -c:a aac -b:a 96k \
-metadata title="Sample Movie One" \
"/opt/jellyfin/media/movies/Sample Movie One (2026)/Sample Movie One (2026).mp4"
# 电影 2:HEVC 1280x720 45 秒(用于触发转码场景)
ffmpeg -hide_banner -loglevel error -y \
-f lavfi -i "testsrc2=size=1280x720:rate=24" -f lavfi -i "sine=frequency=523:duration=45" \
-t 45 -c:v libx265 -preset veryfast -crf 30 -pix_fmt yuv420p -tag:v hvc1 -c:a aac -b:a 96k \
-metadata title="Sample Movie Two" \
"/opt/jellyfin/media/movies/Sample Movie Two (2026)/Sample Movie Two (2026).mp4"
# 测试音乐:mp3 带 ID3 标签
ffmpeg -hide_banner -loglevel error -y -f lavfi -i "sine=frequency=523:duration=180" \
-c:a libmp3lame -b:a 128k -metadata title="Demo Track One" -metadata artist="Demo Artist" \
-metadata album="Demo Album" -metadata date="2026" -metadata track=1 \
"/opt/jellyfin/media/music/Demo Artist/Demo Album (2026)/01 - Demo Track One.mp3"
三条命令均无回显即成功。生成后检查目录树:
find /opt/jellyfin/media -type f | sort
实测回显:
/opt/jellyfin/media/movies/Sample Movie One (2026)/Sample Movie One (2026).mp4
/opt/jellyfin/media/movies/Sample Movie Two (2026)/Sample Movie Two (2026).mp4
/opt/jellyfin/media/music/Demo Artist/Demo Album (2026)/01 - Demo Track One.mp3
💡 说明:影片 2 特意编码为 HEVC,是为了后续 9.4 节触发「客户端不支持 HEVC → 服务端转 H.264」的真实转码场景。
四、部署步骤
4.1 编写 docker-compose.yml
在 /opt/jellyfin/ 下创建 docker-compose.yml(全文照抄,与实测环境一致):
name: jellyfin-12.4-stack
services:
jellyfin:
image: jellyfin/jellyfin:12.4
container_name: jellyfin-12.4-main
restart: unless-stopped
ports:
- "8146:8096"
- "8147:8920"
volumes:
- ./config:/config
- ./cache:/cache
- ./media:/media
devices:
- /dev/dri:/dev/dri
networks:
default:
name: jellyfin-net
driver: bridge
ipam:
config:
- subnet: 10.89.66.0/24
逐项说明:
name: jellyfin-12.4-stack:Compose 项目名,docker compose ps/down按它定位;image: jellyfin/jellyfin:12.4:锁定版本的官方镜像(见 2.1 节);ports:HTTP/HTTPS 映射到宿主 8146/8147,避免与宿主上其他 Web 服务冲突;容器内 8096/8920 是 Jellyfin 默认端口;volumes:配置、缓存、媒体三个持久化挂载(见第五章);devices: /dev/dri:/dev/dri:把 GPU 渲染设备透传进容器,供 VAAPI 等硬件加速使用;无 GPU 的机器可删掉此行(纯软编);networks:自定义网段10.89.66.0/24,避免与宿主上其他 Compose 网段冲突。
4.2 启动与状态验证
cd /opt/jellyfin && docker compose up -d
实测回显:
Container jellyfin-12.4-main Creating
Container jellyfin-12.4-main Created
Container jellyfin-12.4-main Starting
Container jellyfin-12.4-main Started
等约 10 秒让健康检查就绪,再确认状态:
docker compose ps
实测回显:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
jellyfin-12.4-main jellyfin/jellyfin:12.4 "/jellyfin/jellyfin" jellyfin 12 seconds ago Up 12 seconds (healthy) 0.0.0.0:8146->8096/tcp, [::]:8146->8096/tcp, 0.0.0.0:8147->8920/tcp, [::]:8147->8920/tcp
STATUS 显示 Up … (healthy) 即服务就绪。
4.3 启动日志确认
docker compose logs --tail 5 jellyfin
输出节选(实测首尾,时间与线程号每次启动不同):
jellyfin-12.4-main | [01:13:37.481] [INF] [13] Emby.Server.Implementations.ApplicationHost: ServerId: 90473bc9cb7f45d882ef0b2d6dbc0b82
jellyfin-12.4-main | [01:13:37.481] [INF] [13] Emby.Server.Implementations.ApplicationHost: Core startup complete
jellyfin-12.4-main | [01:13:37.481] [INF] [13] Main: Startup complete 0:00:03.8835434
看到 Core startup complete 与 Startup complete 即启动成功。首次启动因要初始化数据库与扫描字体,耗时会略长。
4.4 完成初始化向导
浏览器打开 http://<服务器IP>:8146,进入首启向导,按顺序完成:
- 语言与地区:界面语言选「Chinese Simplified(简体中文)」、元数据国家选「China」、元数据语言选「Chinese」;
- 创建管理员账号:用户名
admin、密码q1w2e3r4(本文演示账号,生产环境请换强口令); - 元数据设置:保持默认(电影/剧集走 The MovieDb);
- 远程访问:按需勾选远程访问与端口自动映射(局域网部署可关闭自动映射),点「完成」进入登录页。
向导完成后用 admin / q1w2e3r4 登录即进入控制台。
4.5 创建媒体库
Web 控制台路径:控制台 → 媒体库 → 添加媒体库,类型选「电影」、路径填 /media/movies,确认后扫描。等价的 API 方式(便于脚本化,实测可复现):
# 登录取令牌
curl -s -X POST http://127.0.0.1:8146/Users/AuthenticateByName \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Client="curl-test", Device="curl", DeviceId="doc-001", Version="12.4.0"' \
-d '{"Username":"admin","Pw":"q1w2e3r4"}'
输出节选(实测首尾,关键字段):
{"User":{"Name":"admin","ServerId":"90473bc9cb7f45d882ef0b2d6dbc0b82","Id":"c53ef19d6b8a41979d31a5c8562ec4d7",...
"Policy":{"IsAdministrator":true,...
"AccessToken":"79c936645c374864bf1c67f938dc27eb","ServerId":"90473bc9cb7f45d882ef0b2d6dbc0b82"}
# 创建 Movies 媒体库(HTTP 204 表示成功)
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
"http://127.0.0.1:8146/Library/VirtualFolders?name=Movies&collectionType=movies&refreshLibrary=true" \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Token="79c936645c374864bf1c67f938dc27eb", Client="curl-test", Device="curl", DeviceId="doc-001", Version="12.4.0"' \
-d '{"LibraryOptions":{"EnableRealtimeMonitor":true,"EnableChapterImageExtraction":false,"PathInfos":[{"Path":"/media/movies"}]}}'
实测回显:
204
复核媒体库清单:
curl -s -H 'Authorization: MediaBrowser Token="79c936645c374864bf1c67f938dc27eb", Client="curl-test", Device="curl", DeviceId="doc-001", Version="12.4.0"' \
http://127.0.0.1:8146/Library/VirtualFolders
输出节选(实测首尾):
[
{
"Name": "Movies",
"Locations": [
"/media/movies"
],
"CollectionType": "movies",
"LibraryOptions": {
"Enabled": true,
"EnablePhotos": true,
"EnableRealtimeMonitor": true,
...
⚠️ 注意:
PathInfos里的/media/movies是容器内路径(对应 compose 挂载的宿主./media子目录),填宿主路径会扫不到内容。
4.6 部署结果验证
curl -s http://127.0.0.1:8146/System/Info/Public
实测回显:
{"LocalAddress":"http://10.89.66.2:8096","ServerName":"jellyfin-lab","Version":"12.4.0","ProductName":"Jellyfin Server","OperatingSystem":"","Id":"90473bc9cb7f45d882ef0b2d6dbc0b82","StartupWizardCompleted":true}
关键字段核对:Version 为 12.4.0、StartupWizardCompleted 为 true 即部署与向导全部完成。ServerName 默认是主机名哈希(如 386688c2e57f),可在 控制台 → 网络 里改为易记名(本文改为 jellyfin-lab)。
五、数据持久化说明
三个 bind mount 决定了「容器可随时销毁重建、数据不丢」:
| 宿主目录 | 容器路径 | 存放内容 | 丢了的后果 |
|---|---|---|---|
/opt/jellyfin/config | /config | 数据库(用户/库/播放状态)、XML 配置、日志、封面元数据、插件 | 等于重装:需重走向导 |
/opt/jellyfin/cache | /cache | 转码分片等临时产物 | 无影响,重启自动重建 |
/opt/jellyfin/media | /media | 媒体文件本体 | 库条目全部失效 |
- 日常备份:停服务后打包
config与media两个目录即可(cache不用备); - 升级/换镜像:
config/cache/media三目录原样保留,新容器直接继承全部状态(9.2 节实测); - 容器销毁(
docker compose down)不会删除 bind mount 数据,只有手动rm -rf宿主目录才会丢。
六、环境变量与容器内约定
Jellyfin 12.4 的业务配置以 XML 文件为准,不通过环境变量注入;镜像内置的环境变量只负责固定目录约定。容器内 env 实查:
docker exec jellyfin-12.4-main env | sort | grep JELLYFIN
实测回显:
JELLYFIN_CACHE_DIR=/cache
JELLYFIN_CONFIG_DIR=/config/config
JELLYFIN_DATA_DIR=/config
JELLYFIN_FFMPEG=/usr/lib/jellyfin-ffmpeg/ffmpeg
JELLYFIN_LOG_DIR=/config/log
JELLYFIN_WEB_DIR=/jellyfin/jellyfin-web
逐项说明:
| 变量 | 值 | 含义 |
|---|---|---|
JELLYFIN_DATA_DIR | /config | 数据根目录(数据库等) |
JELLYFIN_CONFIG_DIR | /config/config | XML 配置目录(system.xml 等) |
JELLYFIN_LOG_DIR | /config/log | 日志目录 |
JELLYFIN_CACHE_DIR | /cache | 转码缓存目录 |
JELLYFIN_FFMPEG | /usr/lib/jellyfin-ffmpeg/ffmpeg | 转码引擎路径 |
JELLYFIN_WEB_DIR | /jellyfin/jellyfin-web | Web UI 资源目录 |
容器内另有 LD_PRELOAD=/usr/lib/jellyfin/libjemalloc.so.2(用 jemalloc 优化内存分配,勿随意去掉)。部署时无需自定义任何环境变量;要改服务器行为,改 config 里的 XML 文件即可(逐项语义见本专栏后续的配置参数详解篇)。
七、配置文件挂载与首启产物
首次启动完成后,宿主 /opt/jellyfin/config/config/ 下会生成核心配置文件(ls 实查):
database.xml
encoding.xml
logging.default.json
system.xml
system.xml:服务器全局配置(语言、续播阈值、扫描间隔等),对应控制台「服务器」各页;encoding.xml:转码配置(硬件加速类型、CRF、线程数等);database.xml:数据库选项(默认 SQLite、LockingBehavior=NoLock);logging.default.json:日志级别模板。
⚠️ 注意:
networking.xml首次启动不会生成——它是「控制台 → 网络」页首次保存后才落盘的文件(实测两台新装实例均如此),不要误判为部署缺失。
八、等价 docker run 命令
不用 Compose 时,以下 docker run 与上文 compose 完全等价:
docker run -d \
--name jellyfin-12.4-main \
--restart unless-stopped \
-p 8146:8096 \
-p 8147:8920 \
-v /opt/jellyfin/config:/config \
-v /opt/jellyfin/cache:/cache \
-v /opt/jellyfin/media:/media \
--device /dev/dri:/dev/dri \
--network jellyfin-net \
jellyfin/jellyfin:12.4
参数与 compose 一一对应:--restart unless-stopped 对应 restart:,-p 对应 ports:,-v 对应 volumes:,--device 对应 devices:。生产编排建议仍用 Compose(可追溯、可批量管理)。
九、常用运维命令
9.1 日常启停与状态
cd /opt/jellyfin
docker compose stop # 停止(容器保留)
docker compose start # 再启动
docker compose restart # 重启
docker compose down # 停止并删除容器(数据目录保留)
down 实测回显:
Container jellyfin-12.4-main Stopping
Container jellyfin-12.4-main Stopped
Container jellyfin-12.4-main Removing
Container jellyfin-12.4-main Removed
⚠️ 注意:若宿主上还有别的容器挂在
jellyfin-net网络里(本文实测环境另有一台实例),down会提示Network jellyfin-net Resource is still in use,网络会留给剩余容器使用,属正常现象。
9.2 升级版本
cd /opt/jellyfin
docker compose pull # 拉取新版本镜像
# 修改 docker-compose.yml 中 image 标签(如 12.4 → 12.5)后:
docker compose up -d # 重建容器,配置/数据自动继承
升级后验证版本号:
docker exec jellyfin-12.4-main /jellyfin/jellyfin --version
实测回显:
Jellyfin.Server 12.4.0.0
💡 技术拓展:升级前若想保守一点,可先
docker tag备份当前镜像 ID,或直接把 compose 标签写回旧版号docker compose up -d一键回滚——因为数据都在宿主 bind mount 里,镜像版本与数据状态是解耦的。
9.3 卸载与清理
cd /opt/jellyfin
docker compose down # 停止并删容器
docker rmi jellyfin/jellyfin:12.4 # 删镜像
rm -rf /opt/jellyfin # 彻底清除数据(确认不再需要后执行)
只做前两步是「卸软件留数据」,三步全做才是彻底清理。
9.4 转码能力验证(HLS 实测)
用 9.2 节拿到的令牌,请求 HEVC 影片(Sample Movie Two)的 H.264 低码率转码流。第一步取主播放清单:
curl -s -H 'Authorization: MediaBrowser Token="79c936645c374864bf1c67f938dc27eb", Client="curl-test", Device="curl", DeviceId="doc-001", Version="12.4.0"' \
"http://127.0.0.1:8146/Videos/a60a496ce79dcfbf1855a9ede3402759/master.m3u8?MediaSourceId=a60a496ce79dcfbf1855a9ede3402759&VideoCodec=h264&AudioCodec=aac&VideoBitrate=500000&VideoMaxWidth=640&TranscodeReasons=ContainerNotSupported"
实测回显(HTTP 200):
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=628000,AVERAGE-BANDWIDTH=628000,VIDEO-RANGE=SDR,CODECS="avc1.424029,mp4a.40.2",RESOLUTION=960x540,FRAME-RATE=24
main.m3u8?MediaSourceId=a60a496ce79dcfbf1855a9ede3402759&VideoCodec=h264&AudioCodec=copy&VideoBitrate=500000&VideoMaxWidth=640&TranscodeReasons=ContainerNotSupported
第二步拉一个分片确认真实转码产物(URL 里的 runtimeTicks 与 actualSegmentLengthTicks 必须带上,缺了会 400,见 10.2 节):
curl -s -H 'Authorization: MediaBrowser Token="79c936645c374864bf1c67f938dc27eb", Client="curl-test", Device="curl", DeviceId="doc-001", Version="12.4.0"' \
"http://127.0.0.1:8146/Videos/a60a496ce79dcfbf1855a9ede3402759/hls1/main/0.ts?MediaSourceId=a60a496ce79dcfbf1855a9ede3402759&VideoCodec=h264&AudioCodec=copy&VideoBitrate=500000&VideoMaxWidth=640&TranscodeReasons=ContainerNotSupported&runtimeTicks=0&actualSegmentLengthTicks=30000000" \
-o /tmp/seg0.ts -w "HTTP %{http_code} %{size_download} bytes\n"
实测回显:
HTTP 200 285196 bytes
服务端日志随即出现完整 ffmpeg 转码命令行(日志节选,实测原文):
[2026-10-27 00:34:53.878 +00:00] [INF] [7] MediaBrowser.MediaEncoding.Transcoding.TranscodeManager: "/usr/lib/jellyfin-ffmpeg/ffmpeg" "-analyzeduration 200M -probesize 1G -i file:\"/media/movies/Sample Movie Two (2026)/Sample Movie Two (2026).mp4\" -map_metadata -1 -map_chapters -1 -threads 0 -map 0:0 -map 0:1 -map -0:s -codec:v:0 libx264 -preset veryfast -crf 23 -maxrate 500000 -bufsize 1000000 -x264opts:0 subme=0:me_range=16:rc_lookahead=10:me=hex:open_gop=0 -force_key_frames:0 \"expr:gte(t,n_forced*3)\" -sc_threshold:v:0 0 -vf \"setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709,scale=trunc(min(max(iw\,ih*a)\,960)/2)*2:trunc(ow/a/2)*2,format=yuv420p\" -codec:a:0 copy -copyts -avoid_negative_ts disabled -max_muxing_queue_size 2048 -f hls -max_delay 5000000 -hls_time 3 -hls_segment_type mpegts -start_number 0 -hls_segment_filename \"/cache/transcodes/333c6f6d2c6b86c5681abe6611f21380%d.ts\" -hls_playlist_type vod -hls_list_size 0 -y \"/cache/transcodes/333c6f6d2c6b86c5681abe6611f21380.m3u8\""
[2026-10-27 00:34:55.465 +00:00] [INF] [22] MediaBrowser.MediaEncoding.Transcoding.TranscodeManager: FFmpeg exited with code 0
命令行要点解读:-codec:v:0 libx264 说明本次走软件转码(实测宿主为 VMware 虚拟显卡,VAAPI 驱动不可用,见 10.4 节);-maxrate 500000 对应请求参数 VideoBitrate=500000;scale=…960… 对应 VideoMaxWidth=640 计算出的等比输出宽 960(640×360 源按 16:9 放大到 960×540 保持码率效率)。
最后用镜像自带 ffprobe 验证分片流参数:
docker exec jellyfin-12.4-main /usr/lib/jellyfin-ffmpeg/ffprobe \
-v error -show_entries stream=codec_name,codec_type,width,height -of csv=p=0 \
/cache/transcodes/333c6f6d2c6b86c5681abe6611f213800.ts
实测回显:
h264,video,960,540
aac,audio
与主清单声明的 RESOLUTION=960x540、CODECS="avc1.424029,mp4a.40.2" 完全一致,转码链路验证闭环。
十、常见问题(FAQ)
10.1 Q1:想纯 API 完成初始化向导,创建管理员的接口返回 404?
实测现象:向导前半程的接口都能直接调用——GET /Startup/Configuration 返回默认值 {"ServerName":"","UICulture":"en-US","MetadataCountryCode":"US","PreferredMetadataLanguage":"en"},POST /Startup/Configuration(改语言地区)与 POST /Startup/RemoteAccess 均返回 204;但 POST /Startup/User(创建向导用户)返回 404 Not Found——12.4 的 API 规范里包含该路由,实现却未暴露。
结论:管理员账号必须在 Web 向导里创建,API 只能完成向导其余步骤。遇到同问题不要反复重试接口,直接走 4.4 节的 Web 流程。
10.2 Q2:拉分片报 400,提示 runtimeTicks/actualSegmentLengthTicks 必填?
直接拼分片 URL(/Videos/{id}/hls1/main/0.ts)但只带转码参数时,服务端返回:
{"errors":{"runtimeTicks":["The runtimeTicks field is required."],"actualSegmentLengthTicks":["The actualSegmentLengthTicks field is required."]}}
解决:从主清单 main.m3u8 里给出的分片 URL 原样使用(它已带上 runtimeTicks=0&actualSegmentLengthTicks=30000000),或手工补齐这两个参数。正常播放器走完整播放流程不会遇到此问题,只有手工脚本直连分片时才会踩到。
10.3 Q3:创建媒体库报 400 The specified path does not exist?
实测踩坑:compose 里把媒体挂载写成相对路径 ./media 时,若 up -d 前该目录不存在,Docker 会自动创建一个空目录挂进去;容器内 /media/movies 就成了不存在的路径,建库接口返回 400,日志原文:
System.ArgumentException: The specified path does not exist: /media/movies.
解决:先在宿主准备好真实媒体目录(3.2/3.3 节),compose 挂载使用绝对路径,再重建容器(docker compose down && docker compose up -d),重试建库即 204。
10.4 Q4:在线元数据(TMDB)拉不到,电影没有海报?
实测现象:服务器无法访问外网时,扫描日志出现:
[2026-10-27 00:32:24.237 +00:00] [ERR] [10] MediaBrowser.Providers.Movies.MovieMetadataService: Error in "TheMovieDb" for "/media/movies/Sample Movie One (2026)/Sample Movie One (2026).mp4"
System.Net.Http.HttpRequestException: All connection attempts failed. (Network is unreachable) (Connection refused) (api.themoviedb.org:443)
影响边界:库扫描、本地识别、转码、播放全部正常,仅在线元数据缺失,条目退化为「文件名/文件夹解析」(实测两部 Sample Movie One/Two (2026) 都正确识别出了名称与年份 2026)。解决:给服务器开通访问 api.themoviedb.org 的网络,或在库设置中改用本地 NFO/图片。
10.5 Q5:VAAPI 已透传 /dev/dri,转码为什么还是走 libx264?
硬件加速是否可用取决于宿主 GPU 驱动,透传设备只是必要条件。实测宿主为 VMware 虚拟机(显卡 vmwgfx),vainfo 报错:
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/vmwgfx_drv_video.so
libva error: va_openDriver() returns -1
vaInitialize failed with error code -1 (unknown libva error)
驱动加载失败时 Jellyfin 自动回落 libx264 软编(encoding.xml 中 HardwareAccelerationType 保持 none),功能不受损、只多耗 CPU。物理机装好 Intel/AMD 驱动后,在 控制台 → 播放 → 转码 里选 VAAPI 即可启用硬编。
10.6 Q6:8146/8147 想换成标准端口?
把 compose 的 ports 改为 "8096:8096" 与 "8920:8920" 后 docker compose up -d 重建即可。反向场景(宿主 8096 已被占用导致启动失败)同理,换任意空闲宿主端口,容器内侧保持 8096 不动——客户端 API 地址只认你映射出去的宿主端口。
十一、参考链接
- 官方容器部署文档:https://jellyfin.org/docs/general/installation/container/
- Docker Hub 镜像页:https://hub.docker.com/r/jellyfin/jellyfin
- GitHub 仓库与 Releases:https://github.com/jellyfin/jellyfin
- 系列阅读:《Jellyfin 12.4 介绍与选型指南》、《Navidrome 0.64.2 Docker Compose 部署指南》