虽然很早就开始 host 博客和个人网站了,但其实一直都停留在 WP 甚至是静态网站的程度,没有真正涉及 self-hosting 的领域,原因主要有两个:1. 和早期自由和平的景象相比,现代互联网是一个极其恶劣的环境,架设个人服务需要应对各种 spam 和 attack;2. 服务器数据备份、软件和系统的维护、升级都很麻烦:如果是频繁维护需要占用大量时间精力;但反过来,假如每三五个月去维护一次,又会忘掉之前的细节需要再从头搞清楚状况。考虑这两方面的因素,到最后还是 host 静态网页可行性最高。
不过今年在偶然的契机下重新了解了一下 self-hosting 的状况,发现现在的情况从各方面来说都有了长足的改善:网络方面现在有了很成熟的 Zero Trust Network Access (ZTNA) 系统,可以直接使用自己的电脑架设服务器,既可以方便地在外网进行访问,又不用把服务器直接暴露到公共互联网上,从安全和花销方面都变得极其友好,只要自己的电脑能跑起来,各种复杂的服务也都可以有;而软件和系统的维护和升级工作则由于 agent 的普及也变得相当容易——或者即使不用 agent,在普通 LLM chatbot 的辅助下,日常维护也会变得轻松很多。
网络和服务器方面,以往通常有两个选择:租用 Virtual Private Server (VPS) 或者使用自己的电脑做服务器。VPS 的好处在于提供商可能会有基本的系统升级、备份等服务,坏处在于提供的服务、服务器的性能、网络带宽等各方面都和租金挂钩,特别是在考虑需要运行比较复杂的服务的时候。如果自己刚好有闲置且配置不错的电脑的话,用自己的电脑做服务器就便宜很多,但主要的坏处在于网络配置。要么只能在自家局域网范围内使用,如果要在外面也能连,大部分情况是需要做端口映射、获取稳定的公共 IP 或者动态 DNS 服务等麻烦操作,最糟糕的是把自己的服务器暴露到公共互联网上会有被入侵的危险,更糟糕的是家里的局域网环境通常都是为了方便大于安全性考虑,所以攻击者一旦进入,有可能会危及整个局域网内的各种设备和服务。
但是使用 ZTNA 系统之后,使用个人电脑作为服务器的麻烦程度和安全隐患都大大减少了。ZTNA 似乎是源自 Google 的 BeyondCorp 发展而来的架构,这里的 zero trust 简单来说就是将网络封锁在“防火墙”后面,除了指定的登录账号之外任何人都无法访问。这和以往自己在应用层架设服务,必须要登录才能使用似乎没有什么区别,但它将隔离点进行了转移:一是变的更底层,在发起任何网络连接之前就需要认证;二是隔离点从个人服务器移动到了 ZTNA 平台的门户,因为在这个架构下面个人服务器并不会暴露任何监听端口,而是通过主动与 ZTNA 门户发起的连接与外界通讯。这极大地减少了服务器和内部网络与外部的接触面积,由(成熟并且被积极维护的)ZTNA 平台的网络隔离来提供安全性保障,不仅免除了你需要自己不断打补丁和加固自己服务器的安全性的麻烦,而且由专业人士积极维护的通常是更安全的。当然没有什么是绝对安全的,但我想普通人大概还是预防一下网络上的随机无差别攻击为主吧(虽然以后 agent 进一步发展之后会出现无差别全自动结合社会工程学的攻击也不一定 🫠)。
在这个架构下除了安全性的提升之外,便利性也得到极大的改善:拿最便利的 Cloudflare 的 Cloudflare Access 为例,只需要在服务器上部署一个 cloudflare 的后台进程,对于普通基于 http/https 的 web 服务,甚至不需要在客户端安装任何软件,就可以直接实现 ZTNA,从任何地方(登录某个指定的 OpenID 登录后)访问自己家里的服务器,不需要暴露公网 IP 或者入站端口。不过 Cloudflare 的免费版服务有流量和内容(例如限制流媒体等)的限制,而且如果要使用 SSH 或者其他非标准端口的自定义服务的话,还是需要在客户端安装软件。如果有这方面的需求的话,直接选择 Tailscale、NetBird、Pangolin 或者其他许多各种类似的基于 WireGuard 解决方案。虽然需要安装客户端听起来很麻烦,但这套系统似乎已经相当成熟了,例如 Tailscale 的客户端甚至在 iOS 之类沙盒限制很严的系统上也可以轻松安装并使用,而且基于 WireGuard 协议不需要像传统 VPN 那样时刻保持心跳发包(耗电),默认采用分离隧道(Split Tunneling)模式,让懒人甚至可以直接长期挂着 VPN 也不会影响日常互联网访问。
我正好有一个闲置的 M1 Mac Mini,犹豫了一下要不要装成 Asashi Linux,Linux 无疑比 MacOS 更适合做 server,不过 MacOS 也还勉强可用,因为我还用闲置的 Thunderbolt dock 把一大堆闲置的旧移动硬盘都连到系统上做不同服务的外部存储,考虑到大堆需要持续供电的外界设备的稳定性,还是决定直接使用原生系统。ZTNA 配置很简单,直接安装 Tailscale,然后登录。在家里的时候可以直接使用局域网访问服务器,外网的时候客户端需要安装 Tailscale,并登录到同一个网络即可访问。在 Tailscale 的管理界面可以选择允许指定的一些用户加入自己的网络。
网络虽然联通了,但是使用 IP
地址访问自己的服务器也还是很麻烦,好在现在这方面也有很方便的解决方案。例如
Adguard Home
具备 DNS 重写的功能,在自己的服务器上装上它,就可以把自己想要的任意
bar.foo.com 重写为 Mac mini 的 IP
地址,然后需要家里的路由器支持一些定制,把服务器的 IP
固定下来免得需要修改 Adguardhome 的配置,然后将路由器的 DNS 服务器指定为
Mac mini 的 IP。然后再在 Mac mini 上安装一个反向代理,例如简单好用的 Caddy,可以让
book.foo.com 和 music.foo.com 都指向 Mac mini
的 IP 的情况下,让它们分别被映射到系统里跑在不同端口(例如
:8888 和
:9999)的服务。这样在家里的局域网就可以直接使用域名
book.foo.com 访问自己的电子书库了。
那在外网通过 Tailscale 访问的时候呢?也很容易解决,Tailscale 可以使用
Split DNS 帮你把所有 *.foo.com 的 DNS 请求转发给家里的 Mac
mini 来解析,并通过 Subnet Router (子网路由) 来支持直接使用家里的局域网
IP 地址来 routing,这样在外网连上 Tailscale
的情况下也能实现无缝享受家里的网络配置了。具体设置方式就不在这里赘述了,毕竟现在让
LLM 帮忙阅读和整理最新的官方文档很方便。
到这里其实网络环境其实已经非常方便可用了,不过还有一个小问题:现在网络
https 逐渐成为主流,如果使用 iOS
之类非常反人类安全的设备,甚至只允许你在使用裸 IP 的时候用
http,如果使用域名,就必须使用
https。这里碰到的问题是 https
需要浏览器认可的证书,因为我们是使用 Adguard Home 的 DNS
劫持功能瞎写的域名,自然无法提供真正的 book.foo.bar
域名的证书,所以客户端在使用 https
连接的时候会由于证书无效而拒绝连接。这里有两个解决方案:
- 零花销:Caddy 支持自己生成 CA 根证书。麻烦之处在于你需要手工把这个证书导入到你的每个客户端设备上安装并信任。
- 最方便:如果你已经有一个自己的域名,例如
my-server.com,那就简单很多了,Caddy 有一个叫作 DNS Challenge 的机制,可以让 Let’s Encrypt 给它办法合法的免费证书。过程大概是 Caddy 发现自己没有https://book.my-server.com的证书的时候,会自动去找 Let’s Encrypt 申请证书,Let’s Encrypt 会要求 Caddy 在 DNS 里添加一条特殊的 TXT 记录以证明这个域名的拥有权。这里需要你的真实域名服务商(不需要和购买商是同一家)支持 API 方式让 Caddy 可以自动完成这个操作(例如 Cloudflare 的 DNS 可以使用caddy-dns/cloudflare这个插件),验证成功之后 Let’s Encrypt 将给 Caddy 颁发合法证书,这样一来客户端不用做任何特殊操作就可以通过https来访问自己的服务了。
如此一来在网络层面基本上可以实现非常舒服、配置简单且安全性比较有保障的个人服务器配置了,而且使用自己的电脑,跑一些复杂一点的服务在性能上通常也不是大问题,需要升级也是一次性投入。再来看服务和软件层面。以往 self-hosting 所面临的软件更新和维护麻烦的问题,现在都得到了很大程度的简化,懒人方案是直接交给 agent 助手部署,怕捅娄子的同学,使用 Chatbot 老师辅助,自己进行各种配置和操作也比以往顺畅了很多:因为碰到各种意外情况可以很容易让老师或者助手帮忙分析并寻找解决方案。这样即使是 tech stack 比较复杂的服务也不用太担心维护和更新麻烦而拒绝了。
同时现在 agentic coding 的盛行使得本身各种(开源)软件的数量和品类也暴增,其中有不少是 self-hosting 爱好者根据自己的实际需求做出来的东西,可能能找到一些稀奇古怪的切合自己需求的东西,或者甚至自己动手定制一个。当然 vibe coding 本身也是双刃剑,新涌现的开源项目可能大部分都是“AI Slop”规格的,但它也确实极大地增加了优质项目的迭代速度。感觉至少从现阶段的 coding agent 的能力来看,如果作者本身没有对编程和软件系统比较有经验的话,确实很难控制代码库的质量不会随着迭代次数逐渐劣化。而且即便 LLM 的能力大幅提升,一个高质量、成熟的开源项目还是需要持续的热情和长期的精力投入:因为项目本身需要不断随着相关技术、用户反馈、功能更新等原因迭代和更新,而且还有用户社群(bug、反馈等)和开发者社群(pull request 等)的参与等等。有一些粗看不错的项目很快就疏于维护被作者抛弃了。
再就是现在很多服务都会提供标准的 Docker compose 的部署方式,安装、更新、卸载都很方便,现在 MacOS 下使用 OrbStack 来运行 docker container 也没有很笨重。
虽然没有 self-hosting 社区(例如 reddit 组)里很多人那样搞几十上百个服务的 home-server,但我几个月下来也逐渐积累了一些服务,其实真正对自己有用的服务也并没有那么好找,这里列举几个作为记录:
- Navidrome:稳定、轻量的音乐、音频服务,支持 subsonic 协议,不论是 iOS 还是 Android 甚至各种 smart watch 上通常都能找到不错的客户端,随时访问自己的音乐库或者语言学习之类的音频资料。
- Audiobookshelf:同上类似,因为已经比较老牌了,所以有不少客户端直接支持它的协议,可以在各种设备上访问自己的有声书。
- Komga:漫画和电子书服务器,其他类似的服务有不少,比如 Kavita、Stump,或者更专注于电子书的 Grimmory、BookOrbit 等。各有优缺点,我还没有找到一个各方面都很完美的解决方案。Komga 存在时间比较久,有不少第三方客户端可以直接使用它的协议读取漫画,同步进度等。比如在 Boox 的 Android 电子墨水平板上装上 Mihon,加上 Komga 插件就可以连到自己的漫画服务器上看漫画了。所以 Grimmory 之类的 server 端甚至提供模拟 Komga API 的功能。另外现在不少电子书库服务都支持 OPDS 协议,很多兼容的电子书阅读器 app 都可以直接通过该协议浏览和下载电子书。如果要同步阅读进度甚至是高亮、笔记,则需要功能更全的协议,比较常见的两个是:1. KOReader 协议,KOReader 本身可以安装到(越狱的)Kindle 或者安卓系统上,功能强大,不过 UI 设计感觉很难搞懂。2. Kobo 的 eink 阅读器原生支持的协议:Kobo 在这方面做得很开放,用户把阅读器上的 API endpoint 改成自己的服务器就可以。
- 再就是几个定制的服务,基本上完全是 vibe coding 的结果,不过虽然我不太在意具体实现,但代码结构和用户端功能取舍还是经过不少迭代才慢慢成形。感觉在以往是需要非常多时间和精力才能完成的事,现在可以以非常不可思议的速度完成。比如类似于之前的旧平板重用项目,现在做了一个 server+client 结构的版本,照片、视频等素材都可以直接放在服务端,这样管理和同步都方便许多,内容直接通过网络传输到设备上显示,比较 personal 的定制是服务端可以支持随机选择长视频的片段 stream 到客户端,这样自己编辑的旅行出游的长视频也可以放在自家的电子相册中展示。像这样功能比较明确的情况测试和迭代几天基本就可以搞定。另一个我用来 track 自己读书、看电影、玩游戏、画画、旅行以及一些杂项项目(例如 self-hosting)的服务则迭代了很久,一方面自己的需求在很多方面比较模糊,另一方面诸如一个健壮好用的实时预览 iOS Markdown 编辑器这种事情由于 agent 还没有办法有效地测试 UI 交互,总是会碰到各种各样的坑,所以 build 起来也需要不少取舍和迭代。
其他还有不少在 community 里很受欢迎的 self-hosting 服务,比如 Vaultwarden 之类的密码管理器、Google Photos 的开源替代品 immich、扫描文档和收据管理的 Paperless-ngx、自己的 git 仓库 forgejo、Gitea 等。不过我目前的 principle 是 self-hosting 尽量限制在辅助、娱乐等范围,像密码管理器这种 critical service 还是避免 self-hosting,毕竟美国的优质基建下停电还是蛮常见的状况的,万一在外面的时候需要,但家里服务器离线了就很麻烦了。另外,如果开始积累比较多的服务的话,建议认真根据 3-2-1 rule 做好备份工作。