Jellyfin 12.4 Docker Compose 部署指南

在一台 Ubuntu 服务器上用 Docker Compose 从零部署 Jellyfin 12.4:镜像选型与网络渠道、编排启动、初始化向导、媒体库创建、HLS 转码链路实测验证,并沉淀向导 404、建库 400、TMDB 不可达、VAAPI 回落等实测排障案例。

Jellyfin流媒体自托管Docker Compose2026/10/27

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,镜像 ID fea863d0cd9e)
  • 服务器版本实测: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-webWeb UI 静态资源(环境变量 JELLYFIN_WEB_DIR 指向此处)
/usr/lib/jellyfin-ffmpeg/ffmpeg定制构建的 ffmpeg(转码引擎)
/config数据根目录(配置、数据库、日志、元数据、插件都落这里)
/config/configXML 配置文件目录(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,进入首启向导,按顺序完成:

  1. 语言与地区:界面语言选「Chinese Simplified(简体中文)」、元数据国家选「China」、元数据语言选「Chinese」;
  2. 创建管理员账号:用户名 admin、密码 q1w2e3r4(本文演示账号,生产环境请换强口令);
  3. 元数据设置:保持默认(电影/剧集走 The MovieDb);
  4. 远程访问:按需勾选远程访问与端口自动映射(局域网部署可关闭自动映射),点「完成」进入登录页。

向导完成后用 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/configXML 配置目录(system.xml 等)
JELLYFIN_LOG_DIR/config/log日志目录
JELLYFIN_CACHE_DIR/cache转码缓存目录
JELLYFIN_FFMPEG/usr/lib/jellyfin-ffmpeg/ffmpeg转码引擎路径
JELLYFIN_WEB_DIR/jellyfin/jellyfin-webWeb 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 地址只认你映射出去的宿主端口。

十一、参考链接