本文主要说明自托管媒体库服务(以 Octans 为例)如何用一个应用镜像完成私有化部署:SQLite / PostgreSQL、可选 nginx 与 GPU 都通过运行参数或 Compose overlay 选择;并覆盖卷挂载、端口、环境变量占位、就绪探针、硬解设备映射,以及镜像 tag 通道的最小公开口径。
核心交付只有一个应用镜像,同时包含后端、Web 静态资源与专用 FFmpeg runtime。数据库、反向代理和 GPU 不是镜像 tag 维度,而是部署时叠加的运行配置。镜像地址统一写成占位:
<registry>/<image>:<tag>
例如稳定通道:
<registry>/<image>:stable
固定版本部署用 SemVer 固定 tag(Docker tag 去掉 Git 的 v 前缀),不要把 sqlite / postgres / nginx / gpu 写进镜像 tag。
适用范围
覆盖:
- Docker CLI 最小启动(SQLite 默认,或外接 PostgreSQL)。
- 仓库提供的 Compose 参考:SQLite、PostgreSQL(外接库 / 参考库容器)、nginx overlay、GPU overlay。
- 数据卷、媒体只读挂载、权限(
PUID/PGID)、时区、健康检查与基础排障。 - 硬件加速时如何把
/dev/dri/renderD*映射进容器。
不覆盖:
- Kubernetes / Helm。
- PostgreSQL 运维、备份与 HA(见系列中的 PostgreSQL 专文)。
- TLS 证书签发与公网网关完整运维。
- SQLite ↔ PostgreSQL 数据搬迁。
- 完整 CI secrets、runner 权限与手工推送 runbook(附录只保留通道与不可变 tag 原则)。
镜像与通道
| 通道 | tag 形态 | 是否移动 | 用途 |
|---|---|---|---|
| 稳定固定版 | <version>,如 0.2.2 | 否 | 生产可钉死版本 |
| 稳定追溯 | stable-<version>-<shortsha> | 否 | digest 对照与复现 |
| 稳定移动 | stable | 是 | 默认「跟最新稳定」 |
| RC | <version>-rc.<n>、rc、rc-… | fixed 否 / rc 是 | 候选验证 |
| 主线快照 | edge、edge-YYYYMMDDHHMMSS-<shortsha> | edge 是 | 跟踪 main,不建议生产默认 |
原则:
- Git release tag 形如
vMAJOR.MINOR.PATCH,Docker fixed tag 去掉v。 - 稳定版默认跟
stable;需要可回滚时钉 fixed tag 或 trace tag。 - 历史兼容里可能存在的
latest/sha-*不再作为新发布主通道。 - 镜像内同时带 ASP.NET Core runtime 与专用 FFmpeg;运行配置(库、代理、GPU)不拆镜像。
Docker CLI 最小部署
SQLite(默认)
不配置 Database__Provider 时默认 SQLite,库文件在数据卷内:
<data-root>/db/sqlite3/octans.db
docker run -d \
--name octans \
--restart unless-stopped \
-p 7777:7777 \
-e TZ="Asia/Shanghai" \
-v <data-root>:<data-root> \
-v /path/to/media:/media:ro \
<registry>/<image>:<tag>
就绪检查:
curl http://127.0.0.1:7777/api/ready
期望 provider 为 Sqlite(或等价就绪结构),表示已连库并完成 schema readiness。
PostgreSQL(外接库)
只要求能连上 PostgreSQL 16+(自建、云库、宿主机均可):
docker run -d \
--name octans \
--restart unless-stopped \
-p 7777:7777 \
-e TZ="Asia/Shanghai" \
-e Database__Provider=Postgres \
-e Database__Postgres__Url="postgresql://octans:<pg-password>@<postgres-host>:5432/octans" \
-e Database__Postgres__SslMode="Prefer" \
-v <data-root>:<data-root> \
-v /path/to/media:/media:ro \
<registry>/<image>:<tag>
Database__Postgres__SslMode 必须显式配置。常见取值:
| 取值 | 说明 |
|---|---|
Disable | 无 TLS,可信内网 / 本机测试 |
Prefer | 优先 TLS,可协商回退 |
Require | 强制 TLS,不校验证书链 |
VerifyCA / VerifyFull | 强制 TLS 并校验 CA / 主机名 |
云库或强制 TLS 的服务端优先 Require 及以上,不要依赖默认猜测。密码与 URL 可用 secret 文件模式(如 Database__Postgres__UrlFile)注入,勿把真实口令写进公开文档或 git。
端口与监听
容器默认:
LISTEN_IP=0.0.0.0
LISTEN_PORT=7777
只改宿主机端口时改 -p 左侧即可,例如 -p 8096:7777。只有改容器内监听时才同时设 LISTEN_PORT 并改 -p 右侧。普通部署不必手写 ASPNETCORE_URLS;镜像用 ENVIRONMENT / LISTEN_* 生成内部参数。
时区、权限
- 镜像默认
TZ=UTC;日志本地时间建议显式TZ=Asia/Shanghai(或所在 IANA 时区)。业务瞬时时间仍按 UTC 存库,与日志展示时区无关。 - 默认
PUID=1000、PGID=1000、UMASK=0022,只是普通 Linux fallback。 - Unraid / 部分 NAS 常见
PUID=99、PGID=100;最终以数据目录 owner 为准。 - entrypoint 不会对已有宿主机目录递归
chown;数据目录不可写则启动失败并提示修正权限。
查看对齐:
id
stat -c '%u:%g %a %n' <data-root> /path/to/media
Unraid host network 示例要点
host network 下不要再 -p;Octans 直接听宿主机 LISTEN_PORT。硬解时映射 render 节点,例如:
docker run -d \
--name octans \
--net host \
--restart unless-stopped \
-e TZ="Asia/Shanghai" \
-e Database__Provider="Postgres" \
-e Database__Postgres__Url="postgresql://octans:<pg-password>@<postgres-host>:5432/octans" \
-e Database__Postgres__SslMode="Prefer" \
-e LISTEN_IP="0.0.0.0" \
-e LISTEN_PORT="7777" \
-e PUID="99" \
-e PGID="100" \
-e UMASK="0022" \
-v /mnt/cache/appdata/octans:<data-root>:rw \
--device /dev/dri/renderD128 \
<registry>/<image>:stable
媒体目录可后挂(如 -v /mnt/user/media:/media:ro);不挂媒体不影响应用起来与设置初始化。Unraid 模板里的 Repository 写镜像引用本身,不要加 https://。
Compose 叠加模式
Compose 参考文件位于部署树下的 deploy/docker/octans/(路径随仓库布局,以实际 clone 为准)。思路是:基础 compose + 可选 overlay,env 文件只放本机路径与密钥占位。
SQLite compose
复制 env 示例后至少改:
OCTANS_IMAGE=<registry>/<image>:<tag>
OCTANS_DATA_PATH=<data-root>
OCTANS_MEDIA_PATH=/path/to/media
TZ=Asia/Shanghai
docker compose \
--env-file deploy/docker/octans/octans.sqlite.env \
-f deploy/docker/octans/compose.sqlite.yml \
up -d
PostgreSQL compose(外接库)
compose.postgres.yml 只起应用,不起数据库:
OCTANS_IMAGE=<registry>/<image>:<tag>
OCTANS_DATA_PATH=<data-root>
OCTANS_MEDIA_PATH=/path/to/media
TZ=Asia/Shanghai
OCTANS_POSTGRES_URL=postgresql://octans:<pg-password>@<postgres-host>:5432/octans
OCTANS_POSTGRES_SSLMODE=Prefer
docker compose \
--env-file deploy/docker/octans/octans.postgres.env \
-f deploy/docker/octans/compose.postgres.yml \
up -d
叠加参考 PostgreSQL 容器
没有现成库时可叠加 compose.postgres.reference-db.yml(官方 postgres 镜像,仅参考路径):
OCTANS_POSTGRES_PASSWORD=<pg-password>
OCTANS_POSTGRES_DATA_PATH=<data-root>/db/postgres/16
OCTANS_POSTGRES_URL=postgresql://octans:<pg-password>@postgres:5432/octans
OCTANS_POSTGRES_SSLMODE=Disable
docker compose \
--env-file deploy/docker/octans/octans.postgres.env \
-f deploy/docker/octans/compose.postgres.yml \
-f deploy/docker/octans/compose.postgres.reference-db.yml \
up -d
参考库数据目录由 PostgreSQL 容器用户管理,不归应用 entrypoint 管理。生产更常见的是外接已有 PostgreSQL,而不是依赖参考容器。
nginx overlay(可选)
docker compose \
--env-file deploy/docker/octans/octans.sqlite.env \
-f deploy/docker/octans/compose.sqlite.yml \
-f deploy/docker/octans/compose.nginx.yml \
up -d
upstream 指向 compose 内 octans:7777。已有 Caddy / Traefik / 宿主机 nginx 时不必叠该文件,只要把流量指到应用 7777,并保留 /hubs/ 的 WebSocket upgrade。内网单机可直接访问 http://<host>:7777。
GPU overlay(可选)
无 GPU 时容器仍应启动到 ready;硬件能力不可用 ≠ 部署失败。
docker compose \
--env-file deploy/docker/octans/octans.sqlite.env \
-f deploy/docker/octans/compose.sqlite.yml \
-f deploy/docker/octans/compose.gpu.yml \
up -d
默认设备:
/dev/dri/renderD128
宿主机节点不同时:
OCTANS_DRI_DEVICE=/dev/dri/renderD129
entrypoint 会按映射后的 render 设备 GID 把运行用户加入附加组,一般不必手填 render 组 GID。硬解链路与 render 节点从宿主机落到容器 / 虚拟机的细节,见系列中的 Unraid SR-IOV 与 FFmpeg 工具链文。
卷、路径与关键环境变量
建议挂载
| 宿主机路径(示例) | 容器路径 | 说明 |
|---|---|---|
<data-root> | <data-root> | 配置、SQLite、日志、secrets、HLS cache、metadata 等持久化根 |
/path/to/media | /media | 媒体库,建议只读 :ro |
| 参考 PG 数据目录 | 由 PG 容器定义 | 仅 reference-db overlay |
应用侧默认路径示例(可被配置覆盖):
SQLite: <data-root>/db/sqlite3/octans.db
日志: <data-root>/logs/octans.log
认证密钥: <data-root>/secrets/…
HLS session: <data-root>/playback/cache/hls-sessions
metadata: <data-root>/metadata
FFmpeg: /opt/octans-ffmpeg/bin/ffmpeg(镜像内)
Compose 辅助变量(节选)
多数 OCTANS_* 只用于 compose 展开,映射进容器 environment 后才变成应用配置。
| 变量 | 用途 | 备注 |
|---|---|---|
OCTANS_IMAGE | 镜像引用 | 生产写完整 <registry>/<image>:<tag> |
OCTANS_DATA_PATH | 数据目录 | 建议显式 |
OCTANS_MEDIA_PATH | 媒体库 | compose 场景必填 |
OCTANS_HOST_PORT | 宿主机端口 | 默认 7777 |
OCTANS_POSTGRES_URL / SSLMODE | PG 连接 | PG compose 必填 |
OCTANS_POSTGRES_PASSWORD | 参考库密码 | 仅 reference-db |
OCTANS_DRI_DEVICE | render 设备 | 仅 GPU overlay |
OCTANS_NGINX_* | 参考 nginx | 仅 nginx overlay |
容器内常用变量(节选)
| 变量 | 用途 | 默认 / 要点 |
|---|---|---|
TZ | 日志本地时区 | 默认 UTC,建议显式 |
PUID / PGID / UMASK | 运行身份 | 对齐宿主机目录 |
LISTEN_IP / LISTEN_PORT | 监听 | 0.0.0.0:7777 |
ENVIRONMENT | 运行环境简写 | 默认 Production |
Database__Provider | Sqlite / Postgres | 默认 Sqlite |
Database__Postgres__Url | PG URL | PG 必填 |
Database__Postgres__SslMode | TLS 策略 | PG 必填 |
Database__ApplyMigrationsOnStartup | 启动 migration | 镜像默认 true |
Playback__HardwareAcceleration__RenderDevice | 硬解设备 | 自定义 GPU 节点时设 |
| 刮削 API Key 等 | Catalog__Providers__… | 按需,双下划线覆盖配置树 |
Production 下认证签名密钥为空或仍是开发占位时,后端可自动生成并持久化到数据卷 secrets 目录;显式写入过短或占位密钥会启动失败。普通用户不必在文档里抄真实密钥。
配置优先级
环境变量 > 命令行参数 > 数据库 override > User Secrets(非 Production) > appsettings.{Environment}.json > appsettings.json
含义:Docker -e / compose environment 最高;前端配置中心里若某项 locked,多半已被环境变量钉死。排查:
docker inspect octans --format '{{json .Config.Env}}'
docker logs octans
curl http://127.0.0.1:7777/api/ready
健康检查与启动时序
| 接口 | 角色 |
|---|---|
GET /api/ready | readiness:含数据库 / schema 就绪 |
GET /api/health | liveness:进程与 HTTP 管道可响应,不连库 |
/api/ready 成功示例形态:
{
"data": {
"status": "ready",
"provider": "Sqlite"
}
}
镜像 HEALTHCHECK 量级(以当前镜像为准,可按环境调):
interval=10s
timeout=5s
start-period=180s
retries=12
首次启动会在主应用可服务前做 provider 校验与 EF migration。低性能 NAS、机械盘 SQLite、远端 PG 或首次 migration 偏长时,可把 compose 里 start_period 提到 300s。容器 running 但 health 仍 starting 时先看日志,不要只盯 Docker 红点。
网页端「重启服务器」不是 Docker API:应用子进程以约定退出码退出,entrypoint 拉起子进程,容器本身保持运行;docker stop / compose stop 的 TERM 走正常停容器路径。
日志、备份与升级
docker logs -f octans
docker exec octans date "+%Y-%m-%d %H:%M:%S %Z %z"
升级前建议备份:
- 整个数据卷
<data-root>(含 SQLite、secrets、日志与 cache 策略按你的 RPO 取舍) - PostgreSQL 模式下的库备份
- 自定义 env 文件
当前 Web 进程不会在 migration 前自动备份 SQLite。流程应是:停服 / 备份 volume → 换镜像 tag → 启动 → 查 /api/ready 与日志。仅测试库且明确不保留数据时,可走破坏性重建空库再 migration(见 PostgreSQL 专文),生产库勿误用。
常见问题(部署向)
一直 unhealthy
- PG URL / 密码 / host /
SslMode错误,或版本低于要求。 - 数据目录对
PUID/PGID不可写。 - pending migration 但关掉了启动时 migration。
- 首次 migration 超过 healthcheck
start_period。
配置中心改了不生效
环境变量优先级更高;先 docker inspect 环境再查前端 locked 项。
日志时间仍像 UTC
确认容器内 TZ 与 date;若 TZ 对了但 date 仍 UTC,可能是镜像缺 tzdata,需升级镜像或只读挂宿主机 zoneinfo。仅 docker logs --timestamps 前缀为 Z 属于 Docker 驱动行为,与应用日志行内时间无关。
GPU 映射后 FFmpeg / 硬解失败
docker exec octans sh -lc 'ls -l /dev/dri && awk "/^(Uid|Gid|Groups):/ {print}" /proc/1/status'
确认是否叠加 GPU overlay、设备节点是否存在、组权限是否由 entrypoint 补齐。硬解能力验证分层见 FFmpeg 工具链与硬转码系列文。
是否必须用仓库里的 PG / nginx compose
否。正式部署只要求应用能连上 PG 16+(或使用默认 SQLite),以及你自己的入口层能反代到 7777 并处理 WebSocket。
附录:镜像发布通道(公开摘要,非完整 CI)
私有化用户通常只拉取已发布镜像;本节帮助理解 tag 语义,不展开 runner 标签、组织 secrets 与 workflow 文件。
| 通道 | 谁用 | 推送前大致要求 |
|---|---|---|
edge | 跟踪 main | 构建 + 容器 smoke(含 SQLite / PG / compose,有 GPU 时含硬解路径) |
rc | 候选验证 | 同上 + 不可变 fixed/trace tag + 发布说明资产 |
stable | 生产默认移动标签 | 同上;fixed tag 与 stable digest 应对齐 |
公开原则:
- fixed tag 与 trace tag 不可变;已存在则拒绝覆盖,修 bug 发下一版号。
- 发布后应对齐 fixed / trace / moving tag 的 digest,避免「标签指到不同层」。
- 稳定版默认文档入口用
stable;钉版本用 fixed tag。 - 对外文档里的 registry 用
<registry>/<image>:<tag>占位;内网真实主机名、机器人 token、密码不要写进公开文。 - 脱离 CI 的手工推送属于变更控制流程,不在本文展开。
本机拉取与对照(示例):
docker pull <registry>/<image>:stable
docker buildx imagetools inspect <registry>/<image>:stable
边界与注意
- 本文是私有化部署与运行形态说明,不是 Kubernetes 方案,也不是 PostgreSQL / 网关完整运维手册。
- 镜像地址、密码、API Key 一律占位;生产用 secret 管理,勿提交 env 真值。
/dev/dri/renderD*、数据路径、端口以本机为准;硬解依赖驱动与 FFmpeg runtime,见相关阅读。- 升级前备份 volume;migration 不可逆操作前先确认 RPO。
- 配置优先级记住「环境变量压过配置中心」,排障先 inspect 再改 UI。
相关阅读
同系列还可对照:
- 媒体库 FFmpeg 工具链:从 Jellyfin 构建到自维护 runtime:镜像内专用 FFmpeg、能力探测与分层验证。
- Unraid 上给 Ubuntu 虚拟机 SR-IOV 直通 Intel UHD 770 做 VAAPI 转码:render 设备如何从宿主机落到客机 / 容器可用的
/dev/dri。 - Octans PostgreSQL 部署与破坏性重建:外接库、迁移与空库重建口径(与本文 PG 连接配置衔接)。
来源
本文综合改写自 Octans 项目 usage 层 Docker 部署说明与 Docker 镜像 CI 发布说明中的部署与通道口径。镜像仓库主机名、digest、secrets 与 workflow 细节已脱敏为占位或省略;Compose 文件名与环境变量键名保留可操作语义,路径与版本以目标环境验证为准。