付着月费的小型云服务器,总让人觉得有点亏。一名开发者此前把个人服务跑在德国 Hetzner 的一台虚拟主机上,里面装着几个 Web 应用、一个叫 Surf 的远程浏览器、反向代理 Caddy 等。问题出在 Surf 上:最便宜的共享机型还能应付日常,一旦 Chrome 真正开始干活就捉襟见肘,换带独享 CPU 的机型,月费又高得让一个私人浏览器显得不划算。

买新机器也划不来。内存价格高得离谱,组装一台内存宽裕的小主机时机糟糕。就在他翻看二手迷你主机时,想起了那台吃灰的 CMF Phone 1:八颗 ARM 核、8GB 内存、128GB 闪存、Wi-Fi 6、5G 调制解调器,还自带电池。硬件明显被闲置,而且钱早就付过了。
这台手机如今跑着 Surf 及其托管的 Chrome 实例、个人记账工具、屏幕共享服务,外加几个小应用。它们能扛住重启、从 Git 部署,手机在不同网络间移动时依旧可达——也就是说,它真正替代了那台虚拟主机。
第一次走弯路:想刷掉安卓
最直接的思路是刷入常规 Linux 发行版。CMF Phone 1 确实有 postmarketOS 设备移植,能启动,设备页上够多的绿勾让人冲动。这位开发者成了那个冲动的人。
他没太在意那些标红的「已损坏」项:Wi-Fi、蓝牙、硬件加速,以及让这部手机胜任小服务器的几乎一切。结果只看到 postmarketOS 的开机画面和一块黑屏——既不是服务器,也不是手机了。
把原厂 Nothing OS 刷回来又成了另一桩苦差:刷机工具依赖 Windows,他得先在 QEMU 里装 Windows,和 USB 透传、联发科驱动搏斗,看工具卡死,最后挪到真机才恢复出厂镜像。中间一度软砖,只有黑屏,他真以为把一台好端端的设备变成了砖头。
手机救回来了,教训也清楚:安卓已经为这块硬件写好了能用的驱动,Wi-Fi、电源管理、电池、GPU、调制解调器,连同所有古怪的厂商细节都正常工作。为了一个更常规的用户态而全盘抛弃,是笔亏本买卖。
Termux 才是主机
第二次尝试保留原厂安卓,把 Termux 当作宿主环境。它提供了 OpenSSH、runit、Caddy、Cloudflared、包管理,以及足够像样的 Unix 工具;Termux:Boot 在重启后拉起监控进程与 SSH;Tailscale 给手机一个稳定的私网地址,于是从 tailnet 里的任意机器只要敲一句 ssh cmf 就能连上。
Termux 不是虚拟机,它的进程仍跑在安卓的 Linux 内核上,只是它那套基于 Bionic 的用户态和标准 Debian 差别足够大,现成的 Linux 应用镜像没法直接丢进去。这个分裂反而有用:Termux 当小巧的宿主控制面,每个应用自带它期望的 Linux 文件系统。
实际服务由 runit 监管。安卓的省电机制对一台假装服务器的设备极不友好,于是他用 Ansible 给安卓施加一套主机配置:常驻唤醒锁、关闭浅层与深层待机、把 Termux 与 Tailscale 移出后台限制、阻止 Wi-Fi 挂起,并把 Tailscale 设为常驻 VPN。
恢复链路比任何单项设置都重要:
Android 启动
→ Tailscale 常驻 VPN
→ Termux:Boot
→ runit
→ 常驻服务
→ 本地与公网健康检查
从 PRoot 到真正的 chroot
他的应用大多以 Linux ARM64 的 OCI 镜像分发,proot-distro 能在 Debian 下轻松跑起来而不改动应用本身。PRoot 在用户态拦截文件系统和进程操作,让普通 Termux 进程以为自己身处 Debian 根文件系统里。它不是容器边界,仍与安卓共享内核、网络命名空间和 Termux 的用户身份,但作为兼容层极其好用——既不需要 root,也不需要特殊内核。
普通 Web 服务起初这么跑毫无问题。例外是 Surf 这种对延迟敏感的浏览器负载:启动进程、加载库、读取浏览器配置、搬运截屏数据,全都要穿过 PRoot 的用户态翻译层。CPU 明明有富余,Chrome 却够不着。于是他把手机 root 了——不是为了替换安卓,而是为了把同一个 Debian 文件系统正确挂载,用真正的 chroot 进去。runit 依旧掌管生命周期,应用数据仍留在 Termux 存储里;负载只是改用原生系统调用而非 PRoot 抵达安卓内核。提升幅度相当明显。
等这条路走稳,把剩下几个小服务也迁了过去。他的工作站把每个 ARM64 镜像解析到精确的摘要,导出文件系统,Ansible 在手机上校验并安装;一个小的 root 辅助程序建立私有挂载命名空间、以 chroot 进入、降权并启动原镜像的入口。手机上既不需要 Docker,也不需要编译器。这些仍是兼容环境而非安全边界,私有挂载命名空间主要让挂载与清理可预期。
整机变成可复现的状态
到这一步手机什么都能跑,但他不想养一只靠一周后就会忘的命令行历史拼出来的宠物服务器。他把整个宿主搬进 Ansible 管理的状态:版本、服务定义、路由、电源设置、密钥、健康检查,全在同一个私有仓库里。
发布版 / OCI 镜像
→ 在 Git 中固定摘要(digest)
→ 通过 SSH 执行 Ansible
→ 手机上写入带版本号的目录
→ 原子 current 软链接
→ runit 服务
→ 本地与公网健康检查
发布版按摘要固定,装进带版本号的目录,背后是一个原子的 current 软链接;校验和或健康检查不通过就停下部署,回滚只需改回固定版本再应用一遍。应用数据独立于发布版存放。密钥不落在手机上,Ansible Vault 的密文放在基础设施仓库,部署时只把每个服务所需的运行时值渲染进 Termux 的私有存储。
让手机从家用宽带接进流量
下一个要处理的是入口。家用宽带没有虚拟主机那样的静态地址,他也不想通过路由器把 SSH 或随机端口暴露出去,同时仍希望手机拔掉电源、带到别处、连上网络,服务器还在。
HTTP 应用走 Cloudflare Tunnel。cloudflared 从手机发起一条出站连接,Cloudflare 把每个主机名经这条隧道送来,Caddy 再路由到对应的本地回环服务:
互联网
→ Cloudflare Tunnel
→ 127.0.0.1 上的 Caddy
→ 127.0.0.1 上的应用
这些服务没有任何入站路由器规则。cloudflared 只需要一条出站连接,所以手机换到别的网络,隧道会自动重连,主机名也跟着走。Tailscale 在管理面上做同样的事。
Surf 远程浏览器的后端对延迟敏感、自己终结 TLS,而连它的旧 iPad 锁定了服务器身份,普通 Cloudflare Tunnel 在 Cloudflare 一侧终结 TLS 正好踩中它不想要的。解法是把整条 Surf TLS 流裹进一个普通的 WebSocket 里:Cloudflare 转发的是 WebSocket,真正的 Surf 连接仍在其中端到端加密,代价只是多一两个网络来回(iPad 初次测试约 60 毫秒),却能在一条只需出站连接的隧道里跑通。
到底跑了什么
这台机器分两层。安卓与 Termux 掌管硬件、网络、入口与监管;Linux 常驻环境拿到自己期望的文件系统,其余时间不挡宿主的路。
- 安卓 / Termux 宿主:负责硬件、网络、入口、监管,组件含 runit、Tailscale、Caddy、Cloudflared、DDNS、运维仪表盘
- root 后的 Linux 常驻环境:负责应用兼容与负载,运行 Surf 与 Chrome、记账工具、屏幕共享等
最吃力的负载是 Surf,它把现代 Chromium 带到老款 iPhone 和 iPad 上。手机在 Debian 运行时里跑桌面版 Chrome(较新的 arm64 版本)和 Surf 后端,iPad mini 收下 H.264 视频与音频,回传触摸、键盘、标签页和导航指令。另一个常驻项是个人记账工具,记录周期性收支、一次性交易、月度支出上限并逐日推算余额,它的 SQLite 数据库有自动化的离机备份和验证过的恢复流程。最新添的可观测性服务收集八核 CPU、内存、电池、温度与可达性,在 https://dash.cmf 提供一个只能局域网或 tailnet 内访问的界面。
你该照做吗
如果你手边正有一台还算现代、可 root 的 ARM64 手机在吃灰,这事远没有听起来那么荒唐。你得到安静的硬件、低功耗、闪存、Wi-Fi、一块用于救急的内置屏幕,以及一块行为像微型不间断电源的电池。原厂安卓已经支持这块硬件,而 Termux 加一个 root 后的 chroot,足够跑起多得惊人的常规 Linux 软件。
他不会把无可替代的数据放上去而不做自动化离机备份,也不会把 chroot 当成敌意负载的隔离边界。root 扩大了信任边界,安卓仍是个不寻常的服务器宿主,软件渲染的桌面版 Chrome 也打不过带 GPU 的专用工作站。但对手头那几个私人服务,尤其是当替代方案是无限期为一台要么慢、要么贵得离谱的虚拟主机付费时,它是个真正有用的选项,而不只是一个噱头。
来源:Hacker News / seg6.space







