Deploy Mediary Scout
August 1, 2026 · View on GitHub
Mediary Scout 有两种部署方式:
| macOS 桌面版 | Docker Compose (服务器) | |
|---|---|---|
| 适合 | Mac 用户,不想折腾 Docker | NAS / 软路由 / VPS / 闲置 PC |
| 数据层 | SQLite(本地文件) | Postgres |
| 部署 | 下载 DMG,拖进 Applications | docker compose up -d |
| 下载 | GitHub Releases | 本指南下方 |
桌面版:去 Releases 下载 .dmg 或 .exe,打开安装,启动后在 Settings 里配网盘和 LLM 即可。详见 README → Install。
Docker 版:继续往下看。
English summary. Self-host with one command —
docker compose up -dbrings up web (Next.js + in-process worker) + Postgres + a bundled PanSou. Openhttp://<host>:3000, go to Settings, scan-login your drive (115 / Quark / 123 / Tianyi by QR; GuangYaPan by pasted token), add an OpenAI-compatible LLM endpoint, and you're running. To reach it from your phone / TV / on the go, use Tailscale (private mesh — safest) or a Cloudflare Tunnel (public HTTPS, no public IP). Never expose:3000raw to the internet. Full walkthrough below (Chinese).
一行命令起整套:web(Next + 进程内 worker)+ Postgres + 自带 PanSou。本指南覆盖:选宿主 → compose 起服务 → 从自己的设备访问 → 安全/升级。
目录
选择你的宿主
任何能跑 Docker 的常开机器都行。挑一个:
- NAS(群晖 / 威联通 / unRAID) —— 最推荐(常开、省电)。群晖用 Container Manager、威联通用 Container Station、unRAID 用 Community Apps 的 Compose Manager 插件,把本仓库
docker-compose.yml贴进去起即可。pgdata卷落在阵列/SSD 上。 - 软路由(iStoreOS 等) —— 作者实测在带镜像加速的 iStoreOS 上一把过。用 Docker 插件或 ssh 跑 compose;软路由存储小,
pgdata指到外挂盘。 - 闲置 PC / Linux 主机 —— 装 Docker + Compose 插件,
git clone后docker compose up -d。睡眠会停巡检,建议设为常开。 - VPS —— 跑得动;115/夸克转存走网盘服务端,VPS 带宽不影响转存速度,只要能连上网盘 API 即可。⚠️ VPS 在公网,务必看安全。
下面的 compose 步骤在以上任何宿主都一样。
Compose 快速开始
git clone https://github.com/fancydirty/mediary-scout && cd mediary-scout
docker compose up -d # 首次会构建 web 镜像,几分钟
⚠️ 墙内先看这个:Docker Hub 大概率拉不动
本项目有三个镜像来自 Docker Hub(
postgres、构建 web 用的node、cloudflared), 而 Docker Hub 在中国大陆常年不稳定。典型报错:failed to fetch anonymous token: Get "https://auth.docker.io/token...": EOF failed to resolve reference "docker.io/library/node:22-slim" read: connection reset by peer这不是你配置错了,重试也没用(作者实测重试十余次全失败)。在仓库根
.env里 加一行镜像源,三处会一起生效:echo 'DOCKER_MIRROR=docker.1ms.run' >> .env docker compose up -d已实测可用(2026-08-01,大陆直连,四个都能拉到全部三个镜像):
镜像源 备注 docker.1ms.run作者实测首选 dockerproxy.netdocker.m.daocloud.iohub.rat.dev公共镜像站会轮流失效 —— 一个不通就换下一个,别只记住一个。 验证某站可用:
docker pull docker.1ms.run/library/node:22-slim留空(默认)= 直连 Docker Hub 官方,境外网络无需任何设置。
pansou走ghcr.io,不受此变量影响(墙内通常可直连)。另一条路是给 Docker daemon 配全局
registry-mirrors(/etc/docker/daemon.json), 效果一样。DOCKER_MIRROR的好处是只影响本项目、不需要 root、不用重启 daemon。
打开 http://<你的主机>:3000:
- 设置 → 网盘:在品牌瓦片里选一个开始连接——115 / 夸克 / 天翼 / 123 扫码登录,光鸭粘贴 token(见各品牌连接小节);凭证入库后自动用于转存。五个品牌可各绑一块盘,互为独立工作区。
- 就这样。TMDB 元数据经作者 CF Worker 开箱即用(想用自己额度可在设置填 TMDB key);PanSou 网盘搜索源已自带。
组成 / 端口
| 服务 | 镜像 | 说明 |
|---|---|---|
web | 本仓库 Dockerfile | Next.js + 进程内 worker(instrumentation.ts 自启),:3000 |
postgres | postgres:16-alpine | 持久卷 pgdata;表首次查询自建,无需迁移 |
pansou | ghcr.io/fish2018/pansou-web | 网盘搜索源,compose 内经服务名 http://pansou 调用 |
覆盖配置
docker-compose.yml 的 environment: 已设好库连接、PanSou 地址、adapters。要覆盖额外项(TMDB / 115 cookie / LLM / Prowlarr / CID),在仓库根放 .env(参照 .env.example)——compose 会自动加载(缺失也无妨)。
光鸭云盘(GuangYaPan)连接
光鸭云盘(迅雷旗下,2026 年上线)是第三个支持的网盘品牌。它走磁力 / 离线下载优先路径:agent 把 PanSou(magnet 类型)与可选 Prowlarr 找到的磁力 / ed2k / BT 候选,经光鸭的离线下载 API 拉进你自己的盘——和 115 的离线任务路径同理。
⚠️ v1 仅支持磁力 / 离线。 光鸭目前不转存 115 / 夸克 / 光鸭自己的分享链(这类候选会按设计明确报错
GUANGYA_ONLY_MAGNET,不静默失败)。所以光鸭和 Prowlarr 搭配最好(磁力覆盖更全)。和所有获取一样,光鸭也需要先配好 AI 模型(LLM),见下文「想跑真实获取还需要」。
光鸭用 access_token + refresh_token 鉴权(不是 cookie、不是扫码)。access_token 约 2 小时过期,refresh_token 会在过期时自动续期,续期后的新 token 自动写回该盘,无需你手动重粘。
1. 在设置页粘 token
设置 → 网盘连接 → 选「光鸭云盘」标签页。最省事:把下面 Console 打印出来的内容(打印的两段、或它复制到剪贴板的 JSON,都行)整段粘到第一个框,再点框下方的 「识别并拆分 token」,两个框会自动填好;确认无误后点「连接光鸭」。(也可以仍按老办法手动把两个值分别粘进两个框。)连接时会用 token 校验登录态、并在你盘里建好 Mediary Scout/{Movies,TV,Anime} 分类目录。
2. 怎么拿到这两个 token
-
用浏览器登录光鸭云盘网页版:https://www.guangyapan.com(或 app.guangyapan.com),确保已登录。
-
按 F12 打开 DevTools → 切到 Console(控制台) 标签。
-
粘贴并运行下面这段(它从
localStorage里读出登录态。光鸭把凭证存在键credentials_<clientid>,当前 clientid 是aMe-8VSlkrbQXpUR,即键名credentials_aMe-8VSlkrbQXpUR):(() => { const clientId = "aMe-8VSlkrbQXpUR"; // 光鸭 web app 的 client_id const raw = localStorage.getItem(`credentials_${clientId}`); if (!raw) { console.warn("没找到 credentials_* —— 请确认已登录光鸭网页版后重试"); return; } const c = JSON.parse(raw); const out = { accessToken: c.access_token, refreshToken: c.refresh_token }; console.log("accessToken:\n" + out.accessToken); console.log("\nrefreshToken:\n" + out.refreshToken); try { copy(JSON.stringify(out, null, 2)); console.log("\n(已复制到剪贴板)"); } catch {} return out; })();如果
credentials_aMe-8VSlkrbQXpUR取不到(光鸭后续改了 clientid),在 Console 里跑Object.keys(localStorage).filter(k => k.startsWith("credentials_"))看实际键名,把后缀换进上面的clientId。 -
控制台会打印
accessToken与refreshToken(且尝试把{accessToken, refreshToken}JSON 复制到剪贴板)。把打印的两段、或复制的 JSON,整段粘到设置页第一个框,点 **「识别并拆分 token」**即可自动填好两个框(不必手动分辨哪段是哪个)。
🔒 别把 token 贴到任何公开地方(issue、聊天群、截图、粘贴板网站)。它们等同你光鸭账号的登录态;只该出现在你自己实例的设置页里。
3. 来源致谢(API 逆向)
光鸭云盘的网盘 API 集成,基于开源项目 AList 的 guangyapan driver(目录 drivers/guangyapan)。该 driver 是本项目逆向光鸭 API 的来源,在此致谢。
天翼云盘连接
天翼云盘(中国电信)是第四个支持的品牌,走转存分享路径(cloud.189.cn/t/… 分享链,与夸克同模型;无磁力/离线 API,Prowlarr 不适用)。
- 连接:设置 → 网盘 → 选「天翼云盘」→ 用天翼云盘 App 扫码。扫码不便时点开「手动粘 SSON cookie」:浏览器登录 cloud.189.cn 后,从开发者工具 → Application → Cookies 里复制
SSON的值粘入。 - 会话由系统自动续期;显示「掉线」时重新扫码绑定同一账号即可恢复,追踪数据不丢。
- 资源量提示:PanSou 上天翼分享目前偏少(电影尤其弱,剧/动漫可用),见 README 的分享量对比表。
123网盘连接
123网盘是第五个支持的品牌,走转存分享路径(123pan.com/s/… 分享链;免费账号即可转存——转存是服务端秒传复制,不消耗提取流量)。
- 连接:设置 → 网盘 → 选「123网盘」→ 用 123网盘 App 扫码(登录约 90 天有效)。扫码不便时点开「手动粘 token」:浏览器登录 123pan.com 网页版后,开发者工具 → Application → Local Storage 里找
eyJ…开头的登录 token 整段粘入。 - token 到期后显示「掉线」,重新扫码即恢复。
- v1 未启用 123 的磁力离线接口(免费配额极少);候选全部来自 PanSou 的 123 分享。
想跑真实获取还需要
- AI 模型(设置 → AI 模型):填一个 OpenAI 兼容的
baseURL / apiKey / modelId——agent 靠它决策。不填则获取流程无法规划。 - 115 目录 CID(
.env或环境变量):TV_SHOWS_CID/MOVIES_CID/ANIME_CID等落盘父目录。
可选增强
- 自己的 TMDB key(设置 → TMDB 元数据):直连你自己的额度,调不通自动回退作者代理。
- 出站代理(
.env设HTTP_PROXY/HTTPS_PROXY):墙内想用自己的 TMDB token / 额度时用得到。TMDB 的 API 主机(api.themoviedb.org)在国内常被单独墙(官网能开 ≠ API 能通),直连不到你的 token 就用不上。给容器配一个能穿透的代理即可让全部出站请求(TMDB / PanSou / Prowlarr)走它:在仓库根.env里写HTTP_PROXY=http://172.17.0.1:7890和HTTPS_PROXY=http://172.17.0.1:7890(172.17.0.1是 Docker 默认网关,指向宿主机;端口换成你宿主上代理软件的实际端口,如 Clash 的 7890),再docker compose up -d。NO_PROXY可排除内网地址。不设代理时行为不变——TMDB token 留空走作者内置代理依旧开箱即用,这条只为「墙内 + 想用自己 token」准备。- 内置代理也连不上时同样用此法(#83 实例,现已缓解):内置 TMDB 代理曾托管在
*.workers.dev域名下,该域名在部分国内网络/运营商下会被整域阻断——症状是搜索报All N TMDB access(es) failed: TimeoutError(N 为通道数,未配 token 时为 1)。现默认代理已换自定义域名tmdb-proxy.mediaryscout.app,绝大多数国内网络可直连;若你的网络连它也阻断,再按上面配HTTP_PROXY/HTTPS_PROXY让容器出站走代理。 - WSL2 部署注意(#83 踩坑实录):容器内的
127.0.0.1指容器自身,填 Windows 宿主上的代理要用 WSL2 虚拟网卡的宿主 IP;且 Windows 防火墙常拦截来自 WSL2 虚拟网卡的入站连接(即使代理软件开了「允许局域网连接」),需要放行防火墙或在 WSL2 内起一层转发(监听 0.0.0.0 转发到 127.0.0.1:代理端口),容器再指向 WSL2 自身 IP。
- 内置代理也连不上时同样用此法(#83 实例,现已缓解):内置 TMDB 代理曾托管在
- Prowlarr(设置 → 资源提供商):接入索引器聚合,磁力与 PanSou 结果合并,走 115 或光鸭的离线下载落盘(夸克无磁力 API)。
- 换 PanSou 实例(设置 → 资源提供商):默认用 compose 自带的;想指向别的实例/公共域名在此手填。
从你的设备访问
默认 web 只监听宿主的 :3000(局域网内手机 / 电视浏览器直接开 http://<宿主局域网IP>:3000 即可)。想在外网(手机流量、出门在外)也能用,二选一——都不需要公网 IP、都别把 :3000 裸暴露公网:
方式一:Tailscale(私有 mesh,推荐家用)
最简单也最安全。把宿主和你的手机 / 电脑 / 电视都加入同一个 Tailscale 网络(tailnet),它们之间用稳定私有 IP 互访,不经公网、自动加密。
- 宿主装并登录:
curl -fsSL https://tailscale.com/install.sh | sh && sudo tailscale up(NAS 多有现成套件/插件)。 - 手机 / 电脑 / 电视装 Tailscale app,登同一账号。
- 任意设备开
http://<宿主的-tailscale-IP>:3000(或起个 MagicDNS 名字)。
家人也想用:把他们的设备加进你的 tailnet(或用 Tailscale 分享)即可,无需开放任何公网端口。
方式二:Cloudflare Tunnel(需要公网 HTTPS 域名时)
想要一个任意设备浏览器都能直接打开的 https://media.yourdomain.com,又没有公网 IP——用 Cloudflare Tunnel(cloudflared)把宿主出站反连到 Cloudflare,路由器不开任何端口、不暴露公网 IP。本仓库的 docker-compose.yml 已内置一个可选 cloudflared 服务(tunnel profile,默认不启),随栈一条命令起。
前提:一个托管在 Cloudflare 的域名(本指南以 media.yourdomain.com 为例)。
1. 在 Cloudflare 控制台建隧道、拿 token(推荐 dashboard 托管,配置集中、好接 Access)
- Zero Trust → Networks → Tunnels → Create a tunnel → Cloudflared,起个名(如
mediary)。 - 连接方式选 Docker,复制那串连接器 token(
eyJ...一长串)。下一步用它,先不用管它给的 docker 命令。 - 建好后进隧道的 Public Hostname → Add a public hostname:
- Subdomain
media,Domain 选yourdomain.com(→media.yourdomain.com)。 - Service:Type = HTTP,URL =
web:3000。⚠️ 这里必须填 compose 服务名web,不是localhost——cloudflared跑在 compose 网络里,localhost指的是它自己那个容器。 - 保存。DNS 的 CNAME 由 Cloudflare 自动建,无需手动加记录。
- Subdomain
2. 在宿主放 token、随栈起隧道
echo 'TUNNEL_TOKEN=粘贴你的连接器token' >> .env
docker compose --profile tunnel up -d # 多起一个 cloudflared 容器
docker compose logs -f cloudflared # 看到 "Registered tunnel connection" 即通
隧道通了之后,https://media.yourdomain.com 就能从任意设备打开。.env 里的 token 不会进 git(.env 已被忽略)。
隧道连不上 / 老掉线? cloudflared 默认是
auto(优先 QUIC/UDP,理论上会回退 HTTP/2)。但有些网络(部分校园网、运营商、严格防火墙)封/限 UDP 时,auto 未必干净回退,隧道就注册不上或老掉线。这时在.env设TUNNEL_TRANSPORT_PROTOCOL=http2强制走 TCP 上的 HTTP/2,再docker compose --profile tunnel up -d重起即可。
3. ⚠️ 必须有门禁(否则等于把实例裸挂公网)
Mediary Scout 默认单用户、无登录。公网入口必须挡住,二选一:
方案 A(推荐):在应用里设访问密码
设置 → 账号 → 设置访问密码。设好之后:
| 来源 | 行为 |
|---|---|
局域网(手机/电视/电脑直连 :3000) | 免登录,零摩擦 |
| 经隧道从外网访问 | 需要输入密码,登录状态保持 30 天 |
内外之别靠「请求是否带 Cloudflare 注入的头」判定(CF-Ray 等,访客摘不掉)。
⚠️ 这条判定的前提:实例只能经「局域网 + 隧道」两条路到达,不能有第三条。 家庭 NAT 天然满足,但你必须确认:
- 别在路由器上给 web 端口做端口映射 / DMZ;
- 关掉路由器的 UPnP 自动打洞(Emby 曾因「局域网免密 + UPnP 自动打洞」导致数千台服务器被入侵,暴露面正是 UPnP);
- 若把实例放在有公网 IP 的 VPS 上,把 compose 的端口发布改成只监听本机(
127.0.0.1:3000:3000),让隧道从容器网络内访问。一旦存在第三条不经 Cloudflare 的入站路径,那条路上的请求会被判成「局域网」而免登录。
方案 B:Cloudflare Access(前置鉴权,与方案 A 可叠加)
- Zero Trust → Access → Applications → Add an application → Self-hosted。
- Application domain 填
media.yourdomain.com。 - 加一条 Allow 策略,例如 Include = Emails = 你自己的邮箱(进站会先要求邮箱一次性验证码登录)。想给家人用就把他们的邮箱也加进白名单。
- 保存后,任何访问
media.yourdomain.com的人都要先过 Access 这关,才能到达应用。
Access 免费版有 50 个席位上限,超出后按人月计费;只给自己/家人用时方案 A 更省事。
想自启:
cloudflared容器已restart: unless-stopped,宿主重启会自动拉起;隧道配置在 Cloudflare 侧托管,改公网主机名 / Access 策略都在控制台改,不用动宿主。
让 agent 帮你配(方式二提示词) — 把下面这段整体复制给 Claude Code / Codex / opencode,它会带你走完上面整个流程:
你是在帮用户把自托管的 Mediary Scout 暴露到公网,走 Cloudflare Tunnel(无需公网 IP、不开路由器端口)。
前提(不满足就先停,或考虑 Tailscale 替代):
- 用户有一个域名,且其 NS(域名服务器)已切到 Cloudflare——仅在别家注册商买了域名但没切 NS 是不行的。
- 用户能登录 Cloudflare Zero Trust(免费档;注意开通流程可能要求绑一张卡或 PayPal,但不会扣费)。
- 若用户没有域名/不想切 NS/不想绑卡:改建议 Tailscale(私有 mesh,无需域名,适合自己/家人),并停下。
安全红线:
- TUNNEL_TOKEN 是机密,不进 git、不写进文档/截图、不打印日志,只写入实例 .env。
- 禁止在路由器/防火墙上把 3000 端口转发到公网;compose 默认映射到宿主局域网是允许的。
第 0 步·环境门:
1. `docker info` — 失败则提示用户,停止。
2. `docker compose ls` — 找到 mediary-scout 部署目录的 project,`cd` 进去,确认有 web 服务。
第 1 步·建隧道(在 Cloudflare Zero Trust dashboard):
a. Networks → Tunnels → Create a tunnel → 选 Cloudflared → 起名(如 mediary)
b. 连接器页面会给一段 docker run 命令,里面有 token(eyJ... 一长串)。只复制 token 那串,不要复制整条命令。
c. 进这条隧道的 Public Hostname → Add a public hostname:
- Subdomain 填子域(如 media),Domain 选你的域名
- Service: Type=HTTP, URL=web:3000(必须是 compose 服务名 web,不是 localhost)
- DNS CNAME 由 Cloudflare 自动创建,不要手动加记录。
第 2 步·写凭证:
- 确保 .env 中有且仅有一行有效: TUNNEL_TOKEN=<上一步复制的 token>
- 若已有旧 TUNNEL_TOKEN,先用 # 注释备份那行,再写新行;不要动其它配置。
- 若 .env 不在 git 忽略里(git check-ignore .env 返回非 0),先把 .env 加进 .git/info/exclude。
第 3 步·启动:
- 在部署目录执行 `docker compose --profile tunnel up -d`
- 首次会拉取 cloudflared 镜像(慢网络几分钟,正常)。
**墙内若报 `failed to fetch anonymous token` / `connection reset by peer`,重试没用** ——
那是 Docker Hub 常年不稳定,在 `.env` 里加 `DOCKER_MIRROR=docker.1ms.run` 再重跑
(见上方「Compose 快速开始」里的镜像源小节)。`connect.sh` 遇到这个错会直接把解法打出来。
第 4 步·确认连通:
1. 先 `docker compose ps cloudflared` 应显示 Up(若 Starting/空,等 30 秒再看,别判失败)。
2. 再 `docker compose logs cloudflared --tail 30` 看到 "Registered tunnel connection" 即成功。
3. 若 1 分钟后仍无 Registered:
a. token 是否完整一行; b. 目录/服务名 web/cloudflared 与 web 同网络; c. 出站 7844 没被拦;
d. .env 追加 TUNNEL_TRANSPORT_PROTOCOL=http2,重新 `docker compose --profile tunnel up -d`(不是 restart)。
第 5 步·必须加 Cloudflare Access(否则等于裸挂公网):
a. Zero Trust → Access → Applications → Add an application → Self-hosted
b. Application domain 填你的完整子域(如 media.你的域名)
c. 加一条 Policy: Action=Allow,Include 选择器选 **Emails**(注意不是 "Emails ending in"),填用户自己(及家人)邮箱。
d. 登录身份默认走 One-time PIN(邮箱一次性验证码);若登录页提示无 IdP,去 Settings → Authentication 确认 One-time PIN 已启用。
第 6 步·验证(由人来完成):
- 浏览器打开子域 → 应先 Access 邮箱验证 → 通过后进 Mediary Scout。
- 若直接进应用(没拦),立刻回第 5 步检查 policy 是否 Enable、domain 是否精确匹配子域。
- agent 不要自行声称验证结果,让用户确认。
完成后用简短中文汇报:隧道是否 Registered、Access 是否配置、面板是否(由用户确认)能进。
方式三:Mediary Connect(受邀)
若你收到作者的 Mediary Connect 邀请链接(https://mediaryconnect.app/i/...):
- 打开链接,按页显示一次连接信息(含
TUNNEL_TOKEN)。 - 写入实例
.env的TUNNEL_TOKEN=...(可复制页上的 Agent 提示词交给 coding agent 代配)。 docker compose --profile tunnel up -d- 浏览器打开页上的
https://<slug>.mediaryconnect.app,完成 Cloudflare Access 邮箱验证。
Public Hostname 与 Access 已由 Connect 控制面配好,服务目标固定为 compose 内 http://web:3000。
网络不稳时可加 TUNNEL_TRANSPORT_PROTOCOL=http2。
安全
- 本项目只走自部署,作者不托管(见 distribution-and-legal-positioning.md)。默认单用户、无登录。
- 别在公网裸暴露
:3000。要远程用就走上面的 Tailscale(私有)或 Cloudflare Tunnel + Access(带鉴权)。 - 想多人合用同一实例(各绑各的网盘、各看各的库):设环境变量
MEDIA_TRACK_MULTI_USER=1开多用户模式(出注册 / 登录页)。即便开了多用户,也仍建议放在 Tailscale / Access 之后。
多用户与忘记密码
默认单用户、无登录。想让家人 / 朋友合用同一台实例(各绑各的网盘、各看各的库、互相看不见):
- 设环境变量
MEDIA_TRACK_MULTI_USER=1并重启 web(docker compose up -d web)。 - 第一个打开站点的人会看到认领屏:设个用户名 + 密码即成站主。
- 如果这台实例之前已经是单用户、有媒体库了,认领会原样接管现有的库和网盘——不会丢。
- 之后每个人各自在登录页注册自己的账号、连各自的 115 / 夸克。
忘记密码怎么办(本项目不发邮件,无需配 SMTP):
-
普通用户忘了 → 找站主,在「设置 → 账号管理」里一键给他重置密码(不影响他的网盘和媒体库)。
-
站主自己忘了 → 在宿主机上跑(谁能进这台机器谁就能找回):
docker compose exec web node scripts/reset-password.mjs <用户名> [新密码]不给新密码就随机生成并打印。登录后到「设置 → 修改密码」改成你自己的。
即便开了多用户,也仍建议放在 Tailscale / Cloudflare Access 之后——登录只为隔离用户数据,不是给公网当门禁。
国内构建加速(Docker Hub 常年不稳定)
Docker Hub 和 ghcr 在国内常年不稳定,首次 docker compose up 构建 / 拉取会卡住。下面的镜像加速只解决 Docker Hub(占绝大多数镜像);来自 ghcr 的 pansou 是例外,见本节末尾。典型报错(任一即是此问题):
failed to fetch oauth token: Post "https://auth.docker.io/token": ... i/o timeout
DeadlineExceeded / dial tcp ...:443: i/o timeout
解决办法是给 Docker 配一个国内 registry 镜像。按你的平台来 —— ⚠️ 两者方式不同,别搞混:
Docker Desktop(macOS / Windows) — 不是改 daemon.json 文件、也没有 systemctl:
- 打开 Settings(设置)→ Docker Engine;
- 在那段 JSON 里加上
registry-mirrors(和已有字段并列):{ "registry-mirrors": ["https://docker.1ms.run"] } - Apply & Restart(应用并重启),等鲸鱼图标变绿再重试
docker compose up -d。
Linux(含软路由 / NAS,直接装的 Docker Engine):把镜像写进 /etc/docker/daemon.json 的 registry-mirrors,然后 sudo systemctl restart docker:
{ "registry-mirrors": ["https://docker.1ms.run"] }
npm 也慢的话,构建时换国内源:
docker compose build --build-arg NPM_REGISTRY=https://registry.npmmirror.com
镜像地址会失效/限速,
docker.1ms.run只是示例;搜「Docker 镜像加速 可用」找当前能用的即可。配好后,所有 Docker Hub 镜像(postgres、构建 web 用的node、cloudflared)都会走镜像 —— 本仓库 Dockerfile 已特意不写# syntax=指令,避免它绕过镜像、第一步就卡死(见 #46)。
⚠️ 注意 pansou 例外:它来自 ghcr.io(ghcr.io/fish2018/pansou-web),而 Docker 的 registry-mirrors 只对 Docker Hub 生效、管不到 ghcr。若 ghcr 也连不上,二选一:
- 在
.env设PANSOU_IMAGE=指向一个 ghcr 镜像/代理(如ghcr.nju.edu.cn/fish2018/pansou-web:latest,镜像可用性自行确认),再docker compose up -d; - 或者不用自带 pansou —— 把
PANSOU_BASE_URL指到一个外部 PanSou 实例,然后docker compose up -d web(不起 pansou 容器)。
作者实测在带镜像加速的软路由(iStoreOS)上一把过。
升级
./scripts/deploy.sh
scripts/deploy.sh 会 git pull → 重建 web → up -d → 验证跑起来的容器确实是刚拉取的 commit(读容器内 BUILD_COMMIT 和 HEAD 比对,不一致直接报错退出)。等价于 docker compose up -d --build,但多了那道自校验,并且不用 --no-cache。
为什么要自校验? 升级最阴的失败是静默回退:
git pull之后容器仍在跑旧代码,而所有常规信号都在骗你——宿主git rev-parse HEAD显示的是新 commit(和容器里实际跑的代码无关),盯镜像 hash 也没用(--no-cache重建每次 hash 都不同,纯粹是构建不确定性)。#88–#98 就是这样连续五次「部署成功」实则一整天跑旧代码。所以真正的护栏不是缓存技巧,而是一道检查:把镜像构建时刻的 commit 盖进BUILD_COMMIT,部署后比对运行容器的BUILD_COMMIT是否等于HEAD,不等就报错——无论病根是构建缓存、git pull空转、还是容器没被重建,都会当场暴露而非静默溜过。
deploy.sh顺带传GIT_SHA=$(git rev-parse HEAD)作构建参数,Dockerfile 用它在COPY . .前触发缓存失效(ARG 在首次使用时 cache-miss,连带其后各层重建),每换 commit 强制重传源码 + 重建,而慢的npm ci依赖层仍走缓存。不必--no-cache(那会把依赖层也丢掉,慢几分钟)。装了 buildx / 用内置 BuildKit 的宿主本就内容寻址、COPY可靠,这层主要是给用经典构建器(DOCKER_BUILDKIT=0或很老的 Docker)的自部署者兜底;两种构建器下都正确无副作用。手动等价执行 + 校验:
git pull --ff-only GIT_SHA=$(git rev-parse HEAD) docker compose up -d --build # 核对容器真在跑新代码(应等于上面的 HEAD): docker compose exec web cat BUILD_COMMIT
备份与恢复(pgdata)
自托管时 Postgres 数据在 compose 卷 pgdata 里,是媒体库/追踪状态的唯一真相。建议定期备份:
# 在仓库根目录
./scripts/pg-backup.sh ./backups
# → backups/mediatrack-YYYYMMDD-HHMMSS.sql.gz
恢复(会覆盖当前库内容,先停写入/worker):
gunzip -c backups/mediatrack-YYYYMMDD-HHMMSS.sql.gz \
| docker compose exec -T postgres psql -U mediatrack -d mediatrack
也可直接备份 Docker 卷目录(停库后拷贝),但逻辑备份(pg_dump)更便携。
定时备份(host crontab 示例)
在部署机用户 crontab 里加一行即可(路径改成你的仓库目录):
# 每天 03:30 备份 pgdata;保留最近 14 天
# 若要固定北京时间,在 crontab 顶部加: TZ=Asia/Shanghai
30 3 * * * cd /path/to/mediary-scout && ./scripts/pg-backup.sh ./backups >>./backups/cron.log 2>&1
0 4 * * * find /path/to/mediary-scout/backups -name 'mediatrack-*.sql.gz' -mtime +14 -delete
把 backups/ 目录同步到机外(对象存储 / NAS / 另一台机器)再算真正有备份。