个人日志

SV7 部署第一天复盘:为什么我决定重装,并把 Mihomo 提到 Docker 前面

SV7Debian家庭服务器MihomoDockerMoviePilotTailscale115

今天算是 SV7 真正开始从“买回来的一台低功耗小主机”,变成“长期在线基础设施”的第一天。

一天之内,我从 Debian 安装、固定 IP、SSH 和基础系统检查,一路部署到 Docker、Tailscale、CloudDrive2、OpenList,再继续试到 MoviePilot V3。很多服务其实都跑通了,但临近结束时,我反而决定第二天直接重装系统,从头再搭一次。

这并不是因为今天失败了。恰恰相反,正是因为今天已经把大部分坑踩了一遍,才发现第二次可以做得更简单、更干净,也更符合我真正想要的服务器架构。

第一件事:服务器不需要桌面环境

安装 Debian 时,我一开始误选了桌面组件。系统当然能正常使用,GNOME 也不是不能留,但后面越搭越觉得它与这台机器的定位不一致。

SV7 最终会长期放在角落里,主要通过 MacBook SSH 管理。即使完全没有桌面环境,接显示器后依然可以进入本地 TTY 终端,因此图形登录界面并不是 SSH 故障时唯一的救援手段。

今天虽然已经把桌面组件卸掉,也做了残留检查,但这件事让我更明确了一条原则:

对服务器来说,“之后再删掉”不如“一开始就不要安装”。

尤其现在系统还很轻,重装成本很低。与其以后一直怀疑是否还残留无用依赖、目录和配置,不如趁服务尚未正式迁移,重新做一次最小化安装。

所以第二天会直接用现有 U 盘启动盘重装 Debian,安装过程中重新格式化 SV7 的 SSD,不保留今天这套系统作为正式环境。

基础配置其实已经验证成熟

今天并不是白做。

固定 IP、SSH、时区、NTP、用户权限、SSD 检查、系统服务状态,这些基础项已经完整走过一遍。Docker 也成功安装并验证,Compose、Buildx、统一目录权限和 Docker 网络都已经跑通。

Tailscale 同样完成部署,并证明它非常适合作为 SV7 的远程管理保底通道。

这意味着第二次安装时,我已经不再需要边查边试,而是可以把基础系统部分压缩成一套很明确的验收流程:

Debian 最小化安装
→ 固定 IP / SSH
→ 时区与系统更新
→ 磁盘与基础服务检查
→ Tailscale
→ 网络出口
→ Docker

真正变化最大的,反而是“网络出口”和“Docker”之间的顺序。

今天最大的坑不是 Docker,而是 Docker 之前没有代理基础设施

今天多次出现 Docker Hub 拉取重置、超时,甚至换镜像站也不稳定的问题。

一开始的自然反应是继续找国内镜像、换 registry mirror、强制 IPv4,或者怀疑某个容器镜像本身。但实际体验下来,我越来越不喜欢让整个服务器依赖一串随时可能失效的第三方镜像站。

这个问题直接改变了明天的部署顺序。

原来的思路是:

Debian
→ Docker
→ Tailscale
→ 各种媒体服务
→ 最后再考虑代理 / 旁路由

现在改成:

Debian
→ Tailscale
→ Mihomo
→ Docker
→ 媒体服务

Mihomo 会直接原生安装在宿主机,而不是放进 Docker。

这样 Docker Hub、GHCR、GitHub Release 以及后续插件源,都可以从一开始就拥有稳定的出站环境。更重要的是,当 Docker 本身出问题时,代理基础设施不会跟着一起消失。

这解决了一个很典型的“鸡生蛋”问题:

Docker 拉不到镜像
→ 需要代理

如果代理本身又运行在 Docker
→ 代理也拉不下来

把 Mihomo 放在宿主机上,依赖关系会干净很多。

为什么暂时只装 Mihomo,不再叠 GUI

我也比较了 sing-box、Docker OpenWrt,以及 Mihomo 配 MetaCubeXD、TUI 等管理方式。

最后的结论不是“哪个功能最多”,而是继续做减法。

我现在主力机场本身就提供 Clash Party 配置,日常又长期使用 Clash / Surge 这一类策略组和规则体系,因此 Mihomo 的迁移成本很低。同时它以后也能承担部分局域网设备的透明代理和旁路由职责,没有必要现在就为了未来可能出现的复杂网络需求,引入一整套 OpenWrt。

Docker OpenWrt 最大的问题仍然是层级过多,而且代理能力会反过来依赖 Docker;sing-box 很强,但在我当前的使用习惯下,没有带来足以抵消迁移成本的收益。

至于 GUI,目前也先不装。

原因很简单:我后续会比较依赖 fallback 代理组,通过健康检查和自动故障转移减少手动切节点的频率。只要默认策略设计好,日常几乎不需要频繁进入面板。

所以第一阶段 Mihomo 的职责非常克制:

  • 给 SV7 自身提供稳定出站;
  • 给 Docker、GitHub、GHCR 等基础设施提供代理;
  • 保留未来 TUN、透明代理和指定设备旁路由的扩展空间;
  • 后续使用 fallback 代理组尽量自动完成节点故障切换。

先不装 MetaCubeXD,不装 TUI,也不装 Clash Party 桌面端。什么时候真的觉得管理不方便,再加一层也来得及。

CloudDrive2 和 OpenList:跑通了,但第二次不准备直接装

CloudDrive2 今天已经完成 115 挂载,OpenList 也完成部署、登录和 115 接入。两套东西本身都没有问题。

但部署完以后,我反而意识到:现在不应该因为“以前架构里用过”就默认它们必须存在。

新的媒体系统准备以 MoviePilot V3 为自动化核心。如果某个功能 MoviePilot 或 115 插件已经可以直接完成,那么再引入 CloudDrive2、OpenList,只会增加路径映射、权限、故障点和维护面。

因此第二次重构时,它们不会进入基础部署清单。

这不是说以后永远不用,而是把原则改成:

只有出现明确依赖时,再安装对应组件。

比如未来真的需要文件系统语义、大量人工文件操作、某种特定 302 链路,CloudDrive2 或 OpenList 都可以重新加入。但现在没有必要提前为“也许会用到”保留服务。

这是今天非常重要的一次思路变化:服务器不是软件收藏夹,部署成功并不等于应该长期保留。

qBittorrent 也正式从主链移除

qBittorrent 原本计划作为 MoviePilot 的下载执行器,但今天重新梳理资源来源后,这一层同样显得多余。

我的磁力资源长期以来基本都可以直接交给 115 离线下载,而且实际使用中几乎没有遇到“115 下载不了,但本地 BT 可以”的高频场景。

因此新的策略是:

Magnet / ED2K
→ 115 云下载
→ 115 媒体目录

极少数 115 无法处理的任务,再由 Windows 主机临时下载后上传到 115。

这意味着 SV7 不需要为了低概率场景长期运行 qBittorrent,也不需要额外规划本地下载盘、做种、下载目录映射和清理逻辑。

又少了一层。

MoviePilot V3:从“要不要用”变成真正的核心

今天 MoviePilot V3 已经成功跑起来。

过程中也有一个很典型的教训:最开始尝试 PostgreSQL,但因为镜像拉取问题和额外复杂度,最后改成了 SQLite,并保留 Redis。对我这种单用户家庭服务器而言,这种更轻的数据库方案已经足够。

这再次印证了一个方向:

家庭服务器不需要为了“更像生产环境”而主动引入复杂度。

MoviePilot V3 后续会成为媒体自动化的核心,承担资源识别、整理、订阅、115 相关任务、STRM 工作流以及 MCP 接入。

而且我已经决定不再把 Windows 时代的旧 STRM 目录直接当作 SV7 的正式媒体库。

旧目录可以保留做迁移参考和临时备份,但新的正式 STRM 媒体库,会由 MoviePilot V3 按新的目录、命名和生成规则重新构建。

这比把旧结构原封不动搬过来更符合这次重构的意义。

“部署成功”不是目标,“可重建”才是

今天最明显的感受是:一台服务器可以很快堆满服务,但服务越多,并不代表系统越完整。

真正重要的是知道每一层为什么存在。

第一版方案曾经很自然地变成:

Docker
→ Tailscale
→ CloudDrive2
→ OpenList
→ qBittorrent
→ MoviePilot
→ STRM
→ Emby
→ Caddy
→ 更多自动化

看起来每个组件都有理由,但实际部署一遍后才发现,其中不少属于“可能有用”,而不是“当前必需”。

明天的思路会简单很多:

Debian
│
├── Tailscale
│
├── Mihomo
│
└── Docker
     │
     ├── MoviePilot V3
     ├── Redis
     └── 后续真正需要的媒体服务

先把网络、远程管理和容器平台做成稳定底座,然后只从明确需求向上增加服务。

明天的正式重构顺序

重装后,我准备按下面的顺序重新搭建。

第一阶段是 Debian 最小系统。安装时不再选择任何桌面环境,重新格式化 SSD,完成固定 IP、SSH、时区、NTP、系统更新和基础健康检查。

第二阶段是 Tailscale。它仍然放在非常靠前的位置,作为 SSH 和远程排障的保底链路。

第三阶段是宿主机原生 Mihomo。先不承担全屋旁路由,只解决 SV7 自身的稳定出站。配置完成后,再让 Docker daemon、GitHub、GHCR 等明确使用这条出口,同时对局域网和 Tailscale 网段做好直连排除。

第四阶段才安装 Docker Engine 和 Compose。此时镜像拉取不再依赖临时寻找镜像站。

第五阶段部署 MoviePilot V3 和 Redis,沿用 SQLite 方案,先验证核心媒体自动化能力。

之后才进入 STRM、Emby、Caddy、Telegram 等真正的业务服务。每加一层,都先确认上一层已经稳定。

CloudDrive2、OpenList、qBittorrent 暂时全部不进入这一轮基础部署;如果后面出现明确场景,再单独加回来。

后续旁路由也不急着一次做满

Mihomo 的另一个作用,是给未来留下比较自然的升级路径。

第一阶段它只代理 SV7 自己。

等媒体服务器完全稳定后,再考虑让 Apple TV、VR 或其他指定设备把 SV7 作为部分流量的旁路由,而不是一上来就让 SV7 接管全屋 DHCP、DNS、VLAN 和网关。

我现在越来越喜欢这种演进方式:

先解决本机出站
→ 再解决 Docker 出站
→ 再增加指定设备代理
→ 真有必要时才讨论完整软路由

这样网络功能的每一次扩展,都建立在已经稳定的服务器上,而不是一开始就把家庭网络和媒体服务器绑成一个难以排错的大系统。

第一天最大的收获

今天真正有价值的不是装了多少服务,而是把很多“纸面上合理”的设计变成了实际判断。

最终留下来的几条原则很简单:

  1. 最小化安装优于安装后清理;
  2. 网络出口应该早于依赖网络的容器平台;
  3. 基础设施不要反向依赖它要拯救的那一层;
  4. 能少一个长期服务,就少一个故障点;
  5. 不为低概率需求提前部署组件;
  6. 家庭服务器优先追求可理解、可恢复和可重建,而不是堆满企业级组件;
  7. 迁移不是复制旧系统,而是趁机会重新定义哪些东西值得保留。

所以我并不把今天最后决定重装看作返工。

第一遍是在摸清边界,第二遍才是真正开始搭建。

如果第二天能够按照现在这套顺序完成,那么 SV7 的底层结构会比今天早上刚开机时清晰很多:

先让服务器本身稳定,再让 Docker 稳定;先确认需求,再部署服务。