Kyoo 5.2.1 Docker Compose 部署指南

Kyoo 5.2.1 全栈 Compose 部署 0→1 实录:7 服务镜像选型与体积清单、.env 关键项(PUBLIC_URL 与 JWT_ISSUER 联动)、四层启动验证、4 个命名卷与 pg_dump 备份恢复、等价 docker run 逐服务拆解,含 scanner 外网依赖崩溃循环与 /auth/keys 500 空报错等实测排障。

KyooKyoo媒体服务器流媒体自托管2026/10/29

Kyoo 5.2.1 Docker Compose 部署指南

目标:从零完成 Kyoo 5.2.1 的 Docker Compose 全栈部署——镜像选型、.env 配置、启动验证、数据持久化、运维命令与实测排障全部覆盖;所有命令与回显均在 Ubuntu 24.04 LTS + Docker 29.9.2 环境实测取得,7 个服务全部实跑。

一、环境信息

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64)
  • Docker:29.9.2(含 compose 插件 v5.6.1,docker compose version 实测可查)
  • Kyoo:5.2.1(官方镜像 tag 5.2,上游最新稳定版 v5.2.1,2026-08-27 发布)
  • 配套组件:Traefik v3.7(官方镜像)、PostgreSQL 18(官方镜像)
  • 部署目录:/opt/kyoo/(本文演示路径,可任意指定)
  • 入口端口:宿主机 8136 → Traefik 容器 8901(可在 compose 里改回默认 8901,本文演示用 8136 避免占用)
  • 实测部署规模:7 个服务容器、1 个自定义 bridge 网络、4 个命名数据卷

⚠️ 本文主题即 Docker 部署,docker/compose 命令均为正文主体。本系列其他篇目(介绍选型/配置参数/API 使用)见文末参考链接。

二、镜像选型说明

Kyoo 官方只发布容器镜像,不提供裸机安装包,所以镜像选择空间很小、但有两个点必须说清。实测部署使用的镜像清单(docker images 回显,大小为本地实测值):

镜像tag本地大小角色
ghcr.io/zoriya/kyoo_front5.2270MBWeb 前端(Nginx 托管 SPA)
ghcr.io/zoriya/kyoo_api5.2364MB媒体库 REST API
ghcr.io/zoriya/kyoo_auth5.2107MB认证中心(Go)
ghcr.io/zoriya/kyoo_scanner5.21.83GB扫描刮削(Python)
ghcr.io/zoriya/kyoo_transcoder5.29.03GB转码器(内嵌完整 ffmpeg)
traefikv3.7253MB入口网关
postgres18650MB元数据库

选型要点:

  1. 镜像仓库:全部来自官方 GitHub Container Registry(ghcr.io/zoriya/),不要使用第三方转存——scanner/transcoder 内嵌的元数据客户端与 ffmpeg 版本直接决定刮削与播放行为。
  2. tag 口径:官方用 5.2 这种滚动次版本 tag,patch 修复(如 5.2.1)直接滚入同 tag。锁定精确版本需配合 GitHub Release 页对照发布日期。
  3. build 与 image 并存:官方 compose 文件里每个 Kyoo 服务同时写了 build: ./xxx 与 image: ghcr.io/...。首次部署不需要源码:Compose 检测到 image 存在即直接拉取镜像,build: 仅在你显式执行 docker compose build 时才触发。实测本机从未 clone 源码,7 个服务全部由镜像启动。
  4. transcoder 为何 9GB:内嵌完整 ffmpeg 及常用编解码器,属正常体积;磁盘紧张也无法裁剪,这是播放能力的基础。

💡 技术拓展:转码器「硬件加速后端」有多种实现,Kyoo 通过 Compose profiles 提供四档——

后端适用硬件compose profile特点
cpu无要求cpu(默认)兼容性最好,CPU 占用高
vaapiIntel/AMD 核显vaapi需要 /dev/dri 设备直通
qsvIntel 核显qsvIntel Quick Sync,同 vaapi 设备要求
nvidiaNVIDIA GPUnvidia需要 CDI GPU 设备预留

本文因在无 GPU 的环境实测,采用默认 COMPOSE_PROFILES=cpu。

三、前置条件

  1. Docker Engine + Compose 插件。验证命令与实测回显:

    $ docker --version && docker compose version
    Docker version 29.9.2, build a82e528
    Docker Compose version v5.6.1
    
  2. 能拉取 ghcr.io 镜像(约 12.5GB 磁盘余量)。验证方式:第三节的 docker compose pull 成功即证明。

  3. 外网可达 TheTVDB 与 TheMovieDB。这是最容易踩的坑:扫描器启动阶段就要登录 TheTVDB,纯内网环境 scanner 会反复重启(排障实录见第九节 Q1)。验证命令(在任何能联网的机器上):

    $ curl -sI https://api4.thetvdb.com -o /dev/null -w "%{http_code}\n"
    401
    

    返回 401 即代表网络可达(未带 API Key 被拒属正常)。实测内网隔离环境该命令返回 000(连接失败),此时要么给机器开通外网,要么放弃 Kyoo。

  4. 端口未占用:默认 8901(可自行改映射,本文用 8136)。验证:ss -ltn | grep 8136 无输出即可。

  5. 媒体目录:准备一个只读挂载给容器即可的目录,本文用 /opt/kyoo/video/,内放一个测试视频:

    $ ls -lh /opt/kyoo/video/
    total 4.8M
    -rw-r--r-- 1 root root 4.8M Oct 29 01:12 Big Buck Bunny (2008).mp4
    

四、部署步骤

4.1 下载官方两份文件

官方发布物只有两份:docker-compose.yml 与 .env.example。在部署目录直接用 GitHub latest 链接下载:

$ mkdir -p /opt/kyoo && cd /opt/kyoo
$ wget -q https://github.com/zoriya/kyoo/releases/latest/download/docker-compose.yml
$ wget -q -O .env.example https://github.com/zoriya/kyoo/releases/latest/download/env.example
$ ls -l
total 20
-rw-r--r-- 1 root root 6110 Oct 29 01:04 docker-compose.yml
-rw-r--r-- 1 root root 4854 Oct 29 01:04 .env.example

⚠️ 若官方 GitHub 直连超时,可走代理下载:wget -x http://192.168.1.62:7897 <URL>(experiment 实测可用渠道),或从镜像站获取。

4.2 编写 .env

把示例文件复制为 .env(名字必须以点开头、无后缀,compose 的 env_file: ./.env 按字面读它),再按需修改:

$ cp .env.example .env

本文实测使用的 .env 关键项(完整逐项含义见《Kyoo 5.2.1 配置参数详解》篇):

# 媒体库根目录(相对路径则相对 docker-compose.yml 所在目录)
LIBRARY_ROOT=./video
# 转码缓存目录(transcoder 可写,清理后可自动重建)
CACHE_ROOT=./cache
# 图片下载目录
IMAGES_PATH=./images
# 忽略扫描的路径正则(默认忽略 Downloads 目录)
LIBRARY_IGNORE_PATTERN=.*/[dD]ownloads?/.*
# 硬件加速档位:cpu / vaapi / qsv / nvidia
COMPOSE_PROFILES=cpu
# 转码 preset:越慢画质越好(vaapi 档不支持 preset)
GOCODER_PRESET=fast
# 对外访问地址(参与 JWT 签发,必须与浏览器实际访问地址一致)
PUBLIC_URL=http://localhost:8136
# 元数据库连接(默认值即可用;生产环境务必改密码)
PGUSER=kyoo
PGPASSWORD=q1w2e3r4
PGDATABASE=kyoo
PGHOST=postgres
PGPORT=5432

逐项说明:

  • LIBRARY_ROOT:唯一的媒体输入口,挂载进 scanner 与 transcoder 的 /video(只读);
  • CACHE_ROOT:transcoder 的转码缓存(HLS 分片等),实测映射到 /cache,启动/停止/运行期会自动清理,放高速盘最佳;
  • PUBLIC_URL:这是最容易配错的项。api 的 JWT_ISSUER 直接取自它(compose 中 - JWT_ISSUER=${PUBLIC_URL}),auth 签发的 JWT 以它为 issuer——改端口或改域名后必须同步改这里,否则旧 token 全部失效;
  • PG* 五件套:postgres 容器用它初始化库,其余服务用它连库,两边必须一致(compose 的 postgres 服务 - POSTGRES_USER=$PGUSER 等已联动)。

⚠️ API Key 类敏感值不要写进 .env 正文文件入库(如需提交仓库)。运行时用环境变量注入,例如:export KEIBI_APIKEY_SCANNER=$(openssl rand -base64 48) 后在 .env 里写 KEIBI_APIKEY_SCANNER=${KEIBI_APIKEY_SCANNER} 不生效(compose 不会递归解引用宿主环境),正确做法是部署机本地 .env 不进版本库、权限收紧:chmod 600 .env。

4.3 拉取镜像

$ cd /opt/kyoo && docker compose pull

实测输出节选(首次全量拉取约 12.5GB,走 ghcr.io;已拉取过则秒级校验):

 71ffe5b63607 Extracting 1B
 fe5f3d0ae371 Pull complete 0B
 71ffe5b63607 Pull complete 0B
 Image ghcr.io/zoriya/kyoo_scanner:5.2 Pulled

4.4 启动全栈

$ docker compose up -d

💡 说明:up -d 会按 depends_on 顺序先起 postgres 并等其 healthcheck 通过(compose 里写的是 condition: service_healthy),再起 auth/api;transcoder 的具体变体由 COMPOSE_PROFILES 决定(cpu 档起 transcoder 服务)。

4.5 验证部署

四层验证,全部实测:

第一层:容器状态。 7 个服务全 Up、postgres healthy 为止(scanner 见第九节 Q1 的已知外网问题):

$ docker compose ps --format 'table {{.Name}}\t{{.Status}}'
NAME                    STATUS
kyoo-5.2.1-api          Up 48 minutes
kyoo-5.2.1-auth         Up 48 minutes
kyoo-5.2.1-front        Up 48 minutes
kyoo-5.2.1-postgres     Up 48 minutes (healthy)
kyoo-5.2.1-scanner      Up 12 seconds
kyoo-5.2.1-traefik      Up 48 minutes
kyoo-5.2.1-transcoder   Up 48 minutes

第二层:健康检查端点。 api 与 auth 各有 health/ready 两个探针:

$ curl -s http://localhost:8136/api/health && echo && curl -s http://localhost:8136/api/ready && echo && curl -s http://localhost:8136/auth/ready && echo
{"status":"healthy"}
{"status":"healthy","database":"healthy"}
{"status":"healthy","database":"healthy"}

ready 比 health 多一个 database 字段,能确认元数据库连通。

第三层:Web 入口。 浏览器打开 http://localhost:8136 应出现 Kyoo 首页;命令行验证返回 200:

$ curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8136/
200

第四层:注册首个管理员。 第一个注册用户自动获得管理员权限(由 .env 的 FIRST_USER_CLAIMS 定义),实测:

$ curl -s -w "\n[%{http_code}]\n" -X POST http://localhost:8136/auth/users \
    -H 'Content-Type: application/json' \
    -d '{"username":"admin","email":"admin@example.com","password":"q1w2e3r4"}'
{"id":"3dcefdeb-254b-40f9-beeb-e1d7dd547241","createdDate":"2026-10-29T10:13:23.469473Z","lastUsed":"2026-10-29T10:13:23.469473Z","device":"curl","token":"W5zY5ejXbN56Ugj5goFkNkWjNapfZxfJ6_BnQ77EBzjeFWmWoO-Ygs74MGidjqOfuU8md1n2V-G4m7Rfd25_Rw"}
[201]

返回 201 并带 token 字段即部署成功,浏览器用同一账号登录即可使用界面。

五、数据持久化说明

实测 docker volume ls 显示 compose 自动创建 4 个命名数据卷(前缀为项目名规范化后的 kyoo-521-stack_):

卷名容器内挂载点内容丢失影响
kyoo-521-stack_dbpostgres /var/lib/postgresql全部元数据(用户/媒体库/进度)致命:账号与媒体库全丢
kyoo-521-stack_imagesapi /images刮削下载的海报/缩略图可重建(重新刮削)
kyoo-521-stack_profile_picturesauth /profile_pictures用户头像可重建
kyoo-521-stack_transcoder_metadatatranscoder /metadata探测缓存/指纹可重建(自动重探测)

两个 bind mount(跟随 .env 路径,宿主目录实测):

  • ./video → scanner/transcoder /video(:ro 只读):你的媒体文件本体,永远不要让 Kyoo 写这个目录;
  • ./cache → transcoder /cache:HLS 分片等临时缓存,可随时清空。

备份建议:核心是 db 卷。冷备一条命令即可(postgres 官方镜像自带 pg_dump):

$ docker exec kyoo-5.2.1-postgres pg_dump -U kyoo -d kyoo > kyoo-meta-$(date +%F).sql
$ ls -lh kyoo-meta-*.sql
-rw-r--r-- 1 root root 58K Oct 29 18:26 kyoo-meta-2026-10-29.sql
--
-- PostgreSQL database dump
--

⚠️ 备份产物开头含 \restrict 行属 PostgreSQL 18 的 pg_dump 新格式标记,恢复时保持完整文件原样导入即可。

恢复思路:新环境起栈后 docker exec -i kyoo-5.2.1-postgres psql -U kyoo -d kyoo < 备份.sql,媒体目录与 .env 保持原路径即可。

六、环境变量表

以下为官方 .env.example 全部配置项的实测汇总(含义以官方源码与实测行为为准):

变量默认值作用风险提示
LIBRARY_ROOT./video媒体库根目录(scanner/transcoder 只读挂载)相对路径基于 compose 文件目录
CACHE_ROOT/tmp/kyoo_cache转码缓存目录建议改到大盘/高速盘
IMAGES_PATH./images海报图片下载目录api 卷 images 覆盖此值(容器内 /images)
LIBRARY_IGNORE_PATTERN.*/[dD]ownloads?/.*扫描忽略路径正则按需追加 sample 等关键字
COMPOSE_PROFILEScputranscoder 变体:cpu/vaapi/qsv/nvidia切换后需 up -d 重建
GOCODER_PRESETfast转码速度/画质权衡 presetvaapi 档不支持 preset
THEMOVIEDB_API_ACCESS_TOKEN空自备 TMDB read token留空用内置默认
TVDB_APIKEY空自备 TheTVDB API Key留空用内置默认 Key
TVDB_PIN空TVDB 订阅者 Pin可选
PUBLIC_URLhttp://localhost:8901对外访问地址JWT_ISSUER 联动,改动需重建
EXTRA_OIDC_REDIRECT_URLS空额外 OIDC 回调白名单官方应用/loopback 默认放行
OIDC_GOOGLE_*注释态Google OIDC 接入(名称/ClientID/Secret 等 9 项)需 Google 控制台申请
EXTRA_CLAIMS{"permissions": ["core.read","core.play"], "verified": false}新注册用户默认权限verified=false 需管理员手动验证
FIRST_USER_CLAIMS全量权限 JSON首个用户(管理员)权限含 users/apikeys/scanner 全部权限
GUEST_CLAIMS{"permissions": ["core.read"], "verified": true}未登录游客权限置空则全站需登录
PROTECTED_CLAIMSpermissions,verified受保护 claim 名单官方标注 DO NOT change
KEIBI_APIKEY_SCANNER随机串scanner 调 api 用的内置 API Key建议自行重新生成
KEIBI_APIKEY_SCANNER_CLAIMScore.read,core.write上述 key 的权限与 scanner 职责匹配即可
OTEL_EXPORTER_OTLP_ENDPOINT空OpenTelemetry 上报地址不配则跳过 otel 初始化(实测日志可见)
PGUSER/PGPASSWORD/PGDATABASE/PGHOST/PGPORTkyoo/password/kyoo/postgres/5432元数据库连接五件套生产必须改 PGPASSWORD
POSTGRES_URL空整串连接串(含 SSL 参数)配置后优先于五件套
KYOO_URL空front 调用的 API 地址compose 默认注入 http://api:5000/api
CAST_APPLICATION_ID空Chromecast 接收器 ID仅调试用

⚠️ 表中 KEIBI_APIKEY_SCANNER 为认证凭据,生产环境请用 openssl rand -base64 48 自行生成并通过受控渠道注入,不要沿用任何公开示例值。

七、配置参数说明(compose 关键段解析)

官方 compose 文件约 180 行,值得理解的四段:

① 认证中间件链(api 与 transcoder 服务 labels):phantom-token 中间件把请求先转发到 auth 换 JWT——

- "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt"
- "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,Cookie,X-Api-Key,Sec-WebSocket-Protocol"
- "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization"

含义:客户端持会话 token/API Key 请求 /api/、/video 时,Traefik 先调 auth 的 /auth/jwt 换出正式 JWT 再转发给后端,后端只信 JWT。这就是为什么业务服务自身不实现登录逻辑。

② transcoder 锚点复用:文件顶部 x-transcoder: &transcoder-base 定义公共配置,cpu/vaapi/qsv/nvidia 四个服务用 <<: *transcoder-base 继承、仅覆写差异(profile 与硬件设备)。自选硬件档时不要直接改 transcoder 服务的 devices,而应切 COMPOSE_PROFILES。

③ postgres 健康门:

depends_on:
  postgres:
    condition: service_healthy
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U ${PGUSER} -d ${PGDATABASE}"]
  interval: 5s
  retries: 5

含义:auth/api 等到 pg_isready 通过才启动;改 PGUSER/PGDATABASE 时 healthcheck 自动跟随。

④ 端口与入口:traefik 服务 ports: - "8136:8901"(官方默认 8901:8901),容器内 8901 固定不变,宿主侧可任意改;改完必须同步 PUBLIC_URL。

八、配置文件挂载清单

实测从运行容器 docker inspect 汇总的全部挂载(宿主 → 容器):

宿主/卷容器挂载点读写挂到哪个服务
./video(LIBRARY_ROOT)/video只读scanner、transcoder
./cache(CACHE_ROOT)/cache读写transcoder
卷 kyoo-521-stack_transcoder_metadata/metadata读写transcoder
卷 kyoo-521-stack_images/images读写api
卷 kyoo-521-stack_profile_pictures/profile_pictures读写auth
卷 kyoo-521-stack_db/var/lib/postgresql读写postgres
/var/run/docker.sock同路径只读traefik(动态发现路由)

⚠️ docker.sock 挂载意味着 Traefik 能控制 Docker 守护进程,这是 label 动态路由的代价;生产环境应确保宿主 Docker 访问面受控。

九、常见问题(实测排障实录)

9.1 Q1:scanner 容器反复重启,RestartCount 持续增长

现象(实测):docker ps 里 scanner 状态反复回到 "Up Less than a second",其余 6 个服务正常:

$ docker inspect kyoo-5.2.1-scanner --format 'RestartCount={{.RestartCount}}'
RestartCount=99

日志定位(docker logs kyoo-5.2.1-scanner 实测原文节选):

aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host
api4.thetvdb.com:443 ssl:default [Connect call failed ('199.59.148.6', 443)]
ERROR   Application startup failed. Exiting.

根因:官方源码 scanner/scanner/providers/thetvdb.py 中 TVDB Provider 类初始化即建 ClientSession(base_url="https://api4.thetvdb.com/v4/"),且 API Key 有内置默认值兜底(self._api_key = os.environ.get("TVDB_APIKEY") or TVDB.DEFAULT_API_KEY)——不配置 TVDB_APIKEY 也会在启动时连 TheTVDB 登录,启动期连不上就直接退出,被 restart: unless-stopped 拉起后循环。

解决:给部署机开通外网(或配置可达的 HTTPS_PROXY)。实测本环境直连与代理均不可达(curl 返回 exit 7),属于环境限制无法恢复,其余 6 个服务不受影响,扫描功能需待网络恢复后自动生效(scanner 起来后才开始监控 /video)。选型启示:无法访问 TheTVDB/TheMovieDB 的纯内网环境不适合 Kyoo。

9.2 Q2:POST /auth/keys 返回 500 且 message 为空

现象(实测):按直觉把权限放在顶层字段时:

$ curl -s -w "\n[%{http_code}]\n" -X POST http://localhost:8136/auth/keys \
    -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
    -d '{"name":"docdemo","permissions":["core.read"]}'
{"status":500,"message":"","details":null}
[500]

根因与解决:请求体 DTO(官方源码 auth/apikey.go 的 ApiKeyDto)字段是 name + claims,不是 permissions。改成正确结构(实测 201):

$ curl -s -X POST http://localhost:8136/auth/keys \
    -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
    -d '{"name":"docdemo","claims":{"permissions":["core.read","core.play"]}}'
{"id":"ee1691bb-0946-4e90-81a3-8b176121282a","name":"docdemo","claims":{"permissions":["core.read","core.play"]},"token":"y8WgTX_qztDG-uJQbW8vZCp2aVN4g9Y82DE9Ml8HnPsNggaR-DAvPUNnJ_3mIqlagUBDUXwjwxpbwAxqAv3E9w"}

9.3 Q3:部分路径 502 Bad Gateway

现象(实测):scanner 崩溃期间访问其暴露路由:

$ curl -s -w "\n[%{http_code}]\n" http://localhost:8136/scanner/openapi.json
Bad Gateway
[502]

定位与解决:502 = Traefik 找得到路由规则但后端连不上。docker compose ps 看 scanner 在反复重启(见 Q1);修复对应服务后 502 自动消失。通用口诀:404 是没有这条路由、502 是有路由但后端挂了。

9.4 Q4:改了端口/域名后登录失效

根因:.env 的 PUBLIC_URL 是 JWT 的 issuer(compose 里 JWT_ISSUER=${PUBLIC_URL})。改端口或用域名访问后必须同步修改并重建 api/auth:

$ docker compose up -d --force-recreate api auth

9.5 Q5:compose up 报 env 文件找不到

现象:env_file: ./.env 校验失败类报错。

解决:官方 .env.example 必须改名成 .env(无后缀、以点开头),放在 docker-compose.yml 同目录。cp .env.example .env 即可。

十、等价 docker run

理解 compose 配置后,每条命令都能翻译成等价 docker run(先建网络与卷)。以下为 7 个服务的等价命令,可独立用于单服务调试:

# 0. 公共前置:网络与数据卷
docker network create --subnet 10.89.60.0/24 kyoo-5.2.1-net
docker volume create kyoo-521-stack_db
# 1. postgres(healthcheck 与 compose 一致)
docker run -d --name kyoo-postgres --network kyoo-5.2.1-net \
  -e POSTGRES_USER=kyoo -e PGPASSWORD=q1w2e3r4 -e POSTGRES_PASSWORD=q1w2e3r4 \
  -e POSTGRES_DB=kyoo -e POSTGRES_HOST_AUTH_METHOD=trust \
  -v kyoo-521-stack_db:/var/lib/postgresql \
  --health-cmd "pg_isready -U kyoo -d kyoo" \
  postgres:18
# 2. auth(依赖 postgres healthy,挂 JWT 密钥相关环境)
docker run -d --name kyoo-auth --network kyoo-5.2.1-net \
  -e PGUSER=kyoo -e PGPASSWORD=q1w2e3r4 -e PGDATABASE=kyoo -e PGHOST=postgres -e PGPORT=5432 \
  -e PUBLIC_URL=http://localhost:8136 \
  -e EXTRA_OIDC_REDIRECT_URLS=kyoo \
  -v kyoo-521-stack_profile_pictures:/profile_pictures \
  -l "traefik.enable=true" \
  -l "traefik.http.routers.auth.rule=PathPrefix(`/auth/`) || PathPrefix(`/.well-known/`)" \
  ghcr.io/zoriya/kyoo_auth:5.2
# 3. api
docker run -d --name kyoo-api --network kyoo-5.2.1-net \
  -e PGUSER=kyoo -e PGPASSWORD=q1w2e3r4 -e PGDATABASE=kyoo -e PGHOST=postgres -e PGPORT=5432 \
  -e JWT_ISSUER=http://localhost:8136 -e IMAGES_PATH=/images \
  -v kyoo-521-stack_images:/images \
  -l "traefik.enable=true" \
  -l "traefik.http.routers.api.rule=PathPrefix(`/api/`)" \
  ghcr.io/zoriya/kyoo_api:5.2
# 4. transcoder(cpu 档)
docker run -d --name kyoo-transcoder --network kyoo-5.2.1-net \
  -e JWKS_URL=http://auth:4568/.well-known/jwks.json \
  -v /opt/kyoo/video:/video:ro -v /opt/kyoo/cache:/cache \
  -v kyoo-521-stack_transcoder_metadata:/metadata \
  -l "traefik.enable=true" \
  -l "traefik.http.routers.transcoder.rule=PathPrefix(`/video`)" \
  ghcr.io/zoriya/kyoo_transcoder:5.2
# 5. scanner(外网可达时才能稳定运行,见 9.1)
docker run -d --name kyoo-scanner --network kyoo-5.2.1-net \
  -e KYOO_URL=http://traefik:8901/api \
  -e KYOO_APIKEY="$KEIBI_APIKEY_SCANNER" \
  -e JWKS_URL=http://auth:4568/.well-known/jwks.json \
  -e JWT_ISSUER=http://localhost:8136 \
  -v /opt/kyoo/video:/video:ro \
  ghcr.io/zoriya/kyoo_scanner:5.2
# 6. front
docker run -d --name kyoo-front --network kyoo-5.2.1-net \
  -e KYOO_URL=http://api:5000/api \
  -l "traefik.enable=true" \
  -l "traefik.http.routers.front.rule=PathPrefix(`/`)" \
  -l "traefik.http.services.front.loadbalancer.server.port=8901" \
  ghcr.io/zoriya/kyoo_front:5.2
# 7. traefik(挂 docker.sock 做 label 发现)
docker run -d --name kyoo-traefik --network kyoo-5.2.1-net \
  -p 8136:8901 \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  traefik:v3.7 \
  --providers.docker=true --providers.docker.exposedbydefault=false \
  --entryPoints.web.address=:8901 --accesslog=true

逐条说明:单服务调试时按 1→2→3→6→7 顺序即可看到页面;scanner(5)依赖外网与 KEIBI_APIKEY_SCANNER(.env 已有,docker compose exec auth env | grep KEIBI 可查);transcoder 的 /cache 目录不存在时 Docker 会自动创建。

十一、常用运维命令

全部实测可执行(部署目录 /opt/kyoo 下执行):

# 状态与日志
docker compose ps                                  # 全栈状态(含 health)
docker compose logs -f api                          # 跟看 api 日志(-f 跟随)
docker compose logs --tail 50 transcoder            # 最近 50 行

# 生命周期
docker compose restart scanner                      # 单服务重启
docker compose up -d --force-recreate api auth      # 改 PUBLIC_URL 后重建
docker compose down                                 # 停栈(保留卷)
docker compose down -v                              # 停栈并删卷(彻底重置,谨慎)

# 升级(官方口径:更新两份文件后)
docker compose pull && docker compose up -d         # 拉新镜像并滚动重建

# 进入容器排查
docker exec -it kyoo-5.2.1-postgres psql -U kyoo -d kyoo   # 进元数据库
docker exec -it kyoo-5.2.1-transcoder ffmpeg -version      # 查 ffmpeg 版本

验证回显(down -v 属破坏性命令,演示用 restart 代替验证生命周期命令有效):

$ docker compose restart scanner
[+] Restarting 1/1
 ✔ Container kyoo-5.2.1-scanner  Started

⚠️ docker compose down 不删除命名卷,元数据安全;down -v 会清空用户与媒体库,执行前先按第五节做 pg_dump 备份。

十二、参考链接