cloudflare-os 这名字第一眼看去很容易让人误会,以为又是什么打算替代 Windows 或 Linux 的桌面操作系统。实际上它完全不是那回事——它是一个把个人应用和服务整体搬到 Cloudflare 边缘网络上的"个人云工程",或者说是一套带有系统层设计思路的云端基座。你可以把它理解成:用 Cloudflare 的免费套餐当"内核",把你常用的书签、笔记、定时任务、文件存储、身份认证这些"系统服务"全部跑在边缘节点上,最终组装出一个假的"操作系统"。看起来像个桌面,实际上背后是一堆无服务器函数和分布式存储。
这篇文章适合正在折腾个人项目、想低成本维护一套私有云端服务,但又不愿意整天伺候 VPS 的人。我会从为什么会有这种玩法,到 Cloudflare 产品线怎么当零件用,再到一步步搭建的完整过程和避坑经验,把我踩过的坑一次说清楚。
1. 为什么会有"cloudflare-os"这种东西
1.1 先给"Cloudflare OS"一个定义
所谓 OS,在传统意义上得管硬件、进程、文件系统,再加一层进程间通信和用户界面。但 cloudflare-os 这里借用了"OS"的抽象含义:它不做底层资源调度,它做的是把 Cloudflare 这个分布式边缘网络包装成一套个人应用运行环境。
简单说,你不再拥有一台具体服务器,而是把应用拆成一个个 Worker 函数,部署到全球三百多个城市的边缘节点;数据不放在本地磁盘,而是放在 KV、D1、R2 这样的分布式存储里;访问控制不靠 Linux 用户和 ssh,而是靠 Cloudflare Access 的零信任规则;更新不靠 ssh 上去敲 systemctl restart,而是 git push 之后由 Cloudflare 自动构建发布。这一套组合拳打下来,体验上确实越来越接近"用云当操作系统"。
我之前折腾过几台廉价 VPS,用来跑书签服务、RSS 订阅、个人 API。结果每个月都得花时间修依赖、打补丁、换证书、查磁盘。后来意识到我这些应用根本不需要"一整台机器"——它们只需要响应请求、存点数据、定时跑一下。于是把目光转向无服务器平台,Cloudflare 是这里面最让我舒服的一个,因为它的 DNS、CDN、Worker、存储全都揉在同一个生态里,配置起来不用在各种控制台之间搬数据。
1.2 它到底解决了什么问题
我自己最大的痛点是不想再给个人项目购买和运维 VPS。一台便宜的 VPS 乍看很香,但真跑起来要操心的内容不少:系统镜像升级、SSH 密钥管理、fail2ban 防暴力破解、防火墙规则、日志轮转、数据库备份。而这些事情跟业务功能本身没有任何关系,纯粹是时间黑洞。
cloudflare-os 这套思路把"运维"压缩到了一个极小的范围。你不用管操作系统补丁,因为根本没有可供你登录的操作系统;你不用管公网 IP 和防火墙,因为 Worker 默认就只有 HTTPS;你也不用担心流量被打爆,因为 Cloudflare 本身就是全球最大的 CDN 之一。你的核心任务只剩下两件事:写业务逻辑、用声明式配置描述路由和存储。对我这种"想快速做出东西"的人来说,这才是最舒服的形态。
当然它也不是银弹。后面我会详细说免费套餐的限制、调试成本、以及不适合重计算/长连接场景的坑。如果你看到这里觉得"这不就是 serverless 吗",其实没错,cloudflare-os 的独特点只是它把多个 serverless 能力组合成了一套类似"系统"的开发模型,并且几乎只依赖一家服务商。
2. 核心技术拼图:Cloudflare 产品线拆解
2.1 计算层:Workers 和 Pages Functions
Workers 是整个 cloudflare-os 的 CPU。它运行在 V8 引擎的 isolate 里,而不是传统容器。每次请求到来时,Cloudflare 会在离用户最近的边缘节点上拉起一个运行时来执行你的代码。这意味着你写代码时不需要关心服务器实例类型和数量,只需要实现一个 fetch 事件入口。
我一般把 Workers 当成两段式架构的路由层。一个项目里可以只部署一个 Worker,然后在里面用 URL 前缀做分发,把 /api 路由交给 JSON 接口逻辑,把剩余的路径直接当成静态资源处理。这样整个应用的入口只有一个 URL,看起来很像在操作一台服务器,实际上每个请求都独立分布在世界各地。
Pages Functions 则是 Workesrs 的"贴前端版"。如果你用 Cloudflare Pages 托管静态网站,可以在项目里建一个 /functions 目录,每一个文件就是一个函数路由。它的编译产物和 Worker 本质相同,但集成了静态资源的响应逻辑,特别适合做一个带登录后台的博客或工具站。
写 Worker 的代码非常简单,但要注意两个基础设施上的差异。第一,它是单线程事件循环模型的强化版,每个 isolate 都有严格的 CPU 时间限制,不能无限循环;第二,全局变量不能跨请求共享,跨请求的状态必须放在 KV、D1 或 R2 里。很多从 Node.js 转过来的人容易在这里栽跟头。
2.2 存储层:KV、D1、R2
存储是 cloudflare-os 里最需要精挑细选的部分。Cloudflare 给了你三种不同口味的存储,选错了会非常痛苦。
第一种是 Workers KV,它是全球复制的键值存储。写入一条数据后,最终会在几百秒内传遍全球节点。正因为这个异步复制特性,它特别适合放配置、用户会话、公开书签这类"偶尔更新、经常读"的数据。我的 cloudflare-os 里所有用户设置都放在 KV 里,读取速度极快,写入频率很低。
第二种是 D1,它基于 SQLite,提供了 SQL 查询能力。D1 的出现让个人项目不再需要外挂一个"真正的数据库",你可以直接在 Worker 里执行 SELECT、INSERT、UPDATE。但需要清楚,D1 也不是传统意义上的中央数据库,它底层同样是分布式架构,强调最终一致性,只是 API 看起来像 SQL。对于个人小应用那点读流量来说完全够用,而且全免费的开销非常诱人。
第三种是 R2,用于对象存储。它和 S3 兼容,但没有出口流量费。我拿它放文件备份、头像、博客配图。相比 KV,R2 可以存大文件;相比 D1,它不关心结构化查询。三种存储分工明确,一张表就能看明白:
| 存储 | 适合数据 | 读写模式 | 典型用途 |
|---|---|---|---|
| KV | 小对象、配置、会话 | 读多写少 | 用户偏好、快速缓存 |
| D1 | 结构化数据 | SQL 查询、中等读写 | 书签、条目、统计 |
| R2 | 大文件、对象 | 写入后较少修改 | 图片、视频、备份 |
2.3 路由与网络:DNS、CDN、WAF
一个完整的 OS 不能没有网络协议栈,Cloudflare 在网络层面恰恰是它的强项。你在 Cloudflare 控制台把自己域名的 DNS 托管过去,它免费帮你代理所有流量,然后在这个前置层做 CDN 缓存、WAF 拦截、自动 HTTPS 证书。整套能力对个人项目来说差不多是"白送"的。
这三个特性叠加起来非常有意思。传统部署要自己申请证书、配置 nginx 反代、处理跨域,到 cloudflare-os 这里全变成开关。DNS 解析的 TTL 可以设为自动,证书自动续签,HTTP/2、HTTP/3 开箱即用。你可以为同一个域名下的不同子域设置不同规则,比如 blog.example.com 走缓存重一点的规则,api.example.com 绕过缓存直接打 Worker。
值得注意的是,Cloudflare 默认代理只认配置的源站。如果你用的是 Worker,源站本身就是 Worker;如果你用 Pages,Cloudflare 会自动把静态资源托管到自身的边缘存储。整个链路不再存在一台具体的机器,网络和安全都移交给边缘层。
2.4 身份层:Access 与 Zero Trust
别忽略身份认证。虽然许多个人项目一开始觉得不需要登录,但把服务暴露在公网上之后,任何人都能访问到你的潘多拉魔盒。cloudflare-os 里我会把 Cloudflare Access 当作"系统用户系统"来用。
Access 允许你定义一套访问规则,谁通过什么邮箱、什么设备可以访问哪条路径。它免费支持最多 50 个用户,对个人用途来说相当于送了一个单点登录服务。你可以只允许你自己的邮箱登录后台管理路径,其他所有路径全部拦截;如果你想分享某个公开页面,再单独在规则里放行。
这样做法比在应用代码里自己搞 JWT 和密码重置简单得多。我自己的经验是:不要重新发明登录系统。Access 的免费额度已经能覆盖绝大多数个人场景,把身份认证放在边缘层,应用代码里连用户表都不用建。后续如果想在 API 里识别用户身份,Access 都会在请求头里注入 jwt 数据,Worker 里只需要解析 header 就行。
3. 从零搭建一个"cloudflare-os"个人云
3.1 初始化项目:用 Wrangler 创建 Workers
一切从 Wrangler 命令行工具开始。Wrangler 是 Cloudflare 官方 CLI,用来初始化和部署 Workers 项目。你只需要装好 Node.js,然后用 npm 全局安装 wrangler:
npm install -g wrangler之后登录账号:
npx wrangler login这条命令会在本地浏览器弹出授权页面,登录之后你的终端就拥有了你账号的部署权限。接着创建一个新项目:
npx wrangler init cloudflare-os cd cloudflare-os初始化时会问你是否使用 TypeScript、是否创建 git,我一般直接选是。生成出来的项目里已经含有一个很基础的src/index.ts,里面导出了一个默认对象,带一个 fetch 方法。你把它理解为"请求入口"就行。本地开发使用:
npx wrangler dev这会起一个本地服务,默认端口 8787。你在浏览器打开http://localhost:8787,做的事情和线上完全相同,只不过计算还跑在本地。这一步非常重要,可以让你快速调功能而不用每改一次就部署一次,省钱还不用等全球传播。
3.2 设计一个"桌面"架构:worker 路由与前后端
我建议你也不要把它做成一个纯 REST API,那种形态不像"操作系统"。我实际的 cloudflare-os 项目是做一个极简 Web 桌面:加载页面后你看到一个桌面壁纸和几个图标,点"笔记本"打开笔记列表,点"书签"打开收藏,点"监控"打开定时任务的结果。所有图标背后都指向同一套前端,再由前端调用/api接口。
Worker 扮演的角色很简单,就是按路径拆分发往不同处理器:
export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); if (url.pathname.startsWith("/api/bookmarks")) { return handleBookmarks(request, env); } if (url.pathname.startsWith("/api/notes")) { return handleNotes(request, env); } // 其他路径统一返回前端静态资源 return env.ASSETS.fetch(request); }, };这里有一个小技巧:我用的是 Worker 的 Sites 绑定,也就是说把前端静态文件直接打包到同一个 Worker 部署里。这样整个项目只有一个 Worker,不需要额外开 Pages,便于统一管理。env.ASSETS指向public目录,构建时自动上传到边缘。你的 Worker 代码加上一堆静态文件,发布之后就等价于一台只有一个"进程"的服务器,但可以服务全世界。
3.3 数据落库:KV 与 D1 的实际使用
先看 KV 怎么用。我在ntconfig.toml里绑定一个 KV namespace:
[[kv_namespaces]] binding = "MY_KV" id = "xxxx"在代码里读写:
export async function getUserMeta(userId: string, env: Env) { const raw = await env.MY_KV.get(`user:${userId}`); return raw ? JSON.parse(raw) : null; }KV 的 key 最好按"业务前缀:实体ID"来设计,像user:001、bookmark:xxx。因为 KV 的 list 接口支持按前缀枚举,这样你可以轻松把一个用户下面的数据全部捞出来。
D1 更适合存书签这类列表。首先创建 D1 数据库:
npx wrangler d1 create cloudflare-os-db然后把数据库 ID 写进ntconfig.toml:
[[d1_databases]] binding = "DB" database_name = "cloudflare-os-db" database_id = "xxxx"之后运行一条迁移命令创建表:
npx wrangler d1 execute cloudflare-os-db --command "CREATE TABLE IF NOT EXISTS bookmarks (id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT NOT NULL, url TEXT, title TEXT, created_at INTEGER)"在 Worker 里写数据:
await env.DB.prepare( "INSERT INTO bookmarks (user, url, title, created_at) VALUES (?, ?, ?, ?)" ).bind(user, url, title, Date.now()).run();读数据:
const { results } = await env.DB.prepare( "SELECT * FROM bookmarks WHERE user = ? ORDER BY created_at DESC" ).bind(user).all(); return new Response(JSON.stringify(results), { headers: { "Content-Type": "application/json" } });这套 API 用起来和平时写 SQLite 没有区别,但它跑的却是全球分布式数据库。我的体会是:单用户或几个人的小项目用 D1 真的舒服,不需要关心连接池,不需要手动建索引,甚至不需要写复杂的 ORM。
3.4 部署流程:替代传统后端的发布方式
cloudflare-os 部署只需要一行命令:
npx wrangler deploy它会把你本地代码和静态资源打包上传到 Cloudflare 全球边缘节点,通常几秒钟之内就能生效。你会得到一个workers.dev结尾的域名,也可以把自定义域名绑定到 Worker 的路由上。
不过我更推荐用 Git 加 Cloudflare Pages 的方式做自动部署。你在 Pages 里导入 Git 仓库,设置构建命令,比如 Vite 或 Hexo,然后在项目的functions目录里写 API 逻辑。每次 push 代码到远端,Cloudflare Pages 会自动构建并发布。千万别小看这个流程,它让你完全摆脱"登录服务器手动部署"的旧习惯,对整个个人云工程来说,这等于拥有了"一键升级系统"的能力。
我一般搭配起来用:文档/管理端用 Pages 托管,复杂 API 接口用独立 Worker 服务。两者可以共享同一个域名,只需要在外层加一条 DNS 记录。不同模块各自独立部署、互不阻塞,这也符合"模块化 OS"的设计。
4. 实操过程中的坑与排查技巧
4.1 免费套餐限制与超限表现
免费套餐是最容易被人忽略的隐形墙。cloudflare-os 适合个人项目,但一旦流量上去,你会撞到 Alan 上的限制。我现在遇到的问题主要集中在三个维度:
- Workers 请求数:免费额度是每天 10 万次请求。看似很多,但如果前端有大量轮询或埋点请求,一下就顶满了。超限后 Worker 会直接返回错误,页面上一会儿能开一会儿不能开,表现非常像"服务器抽风"。
- KV 写入量:KV 免费额度写的次数很少,大概一天 1000 次。如果你每次改配置都写 KV、每次浏览都往 KV 里写当前状态,很快会触发限制。解决办法是把高频状态写进 D1 或干脆用内存缓存,只在最终态才写 KV。
- D1 的读取行数:免费额度有个每天读取行数的上限,超了之后查询会报错。个人项目一般碰不到,但如果写了全表扫描式的 SQL,就要小心了。
要判断自己是否撞了限制,在 Cloudflare 控制台的 Workers 页面能看到每一条告警和请求数图表。设置一个每周用量告警是个好习惯,免得突然挂掉时一头雾水。
4.2 本地调试与线上不一致
Worker 在本地模拟器跑得好好的,一部署就报错,这是最常见的事。我自己踩过的一个典型坑是本地文件系统。Node.js 里你可以随便写操作系统的文件,但 Worker 运行环境没有本地磁盘。所有"写文件"的代码在本地模拟器里可能正常,部署后却悄悄失败。解决办法很简单:从一开始就把所有持久化操作全部放到 KV/D1/R2 上,不要碰本地路径。
另一个坑是环境变量没有同步。你在本地拿.dev.vars里的测试 key 调通了,部署时忘了在控制台配置对应的 production 变量,结果线上一直 401。所以每次换个环境跑之前,第一件事就去查Env对象里缺了什么字段。Wrangler 有一个命令叫npx wrangler tail,可以直接实时打印线上日志,出问题的时候用它看错误信息,比反复改代码靠眼看页面靠谱得多。
4.3 数据库连接与并发写入痛点
D1 虽好,但要记住它本质上是 SQLite 的分布式化身,在写并发上有天生限制。当一个 Worker 实例在执行事务时,如果同时有另一个请求也试图写同一个记录,就可能出现database is locked或者写入冲突。个人项目一般碰不到高并发写,但你如果做的是定时任务加用户手动提交混合的场景,就得留心。
我给"打卡"功能写定时写入时,就遇到过 Cron 触发器和用户手动写入同时发生的锁冲突。解决办法是:把高频写入改成就地 append 到 KV 的一个 list,然后在低峰期用一个任务批量合并到 D1;或者直接在写入前加一个简单的重试机制:
async function runWithRetry(fn: () => Promise<any>, times = 3) { for (let i = 0; i < times; i++) { try { return await fn(); } catch (e) { if (i === times - 1) throw e; await new Promise((r) => setTimeout(r, i * 200)); } } }另外,D1 的免费版是全量读,如果你发现查询越来越慢,优先看是不是少了索引。SQLite 经典的手工加索引在这里同样适用,别指望它自动优化一切。
4.4 跨域、缓存与回源配置
因为 cloudflare-os 整体都是边缘服务,请求大多是浏览器直接发到 API,所以 CORS 是一个绕不开的话题。最基础的处理是在 API 响应里加 CORS 响应头:
const headers = { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", };如果只允许自己的前端调用,别用*,写成你实际使用的域名会更安全。跨域之外,要注意缓存。Cloudflare 边缘默认会对响应做缓存,如果你接口返回的是动态数据,一定要在响应里带上Cache-Control: no-store或者在 Worker 里手动设置请求绕过 cache。否则你会看到前天改的数据到今天还没变,因为边缘缓存把你的响应长期保存了。
强制绕过缓存最稳的办法是在 Worker 代码里直接从原始请求处理,不依赖 Cloudflare 的缓存规则。也可以用 Cache API 手动控制,把高频公共内容主动缓存,而隐私内容一律跳过。缓存策略就跟传统服务器的 HTTP 头管理一样,只是边缘节点更多,一旦缓存错了,影响的范围也更大。
5. 为什么这不算一个真正的操作系统,但还挺好用
5.1 对比 VPS + 宝塔方案,优劣势到底在哪
很多人会问我,用 Cloudflare 这套东西跟传统 VPS + 宝塔面板比,到底哪个香。我把亲身体会整理成一张表:
| 维度 | cloudflare-os | VPS + 宝塔 |
|---|---|---|
| 月度成本 | 基本为 0 | 最低大概 10 元左右起步 |
| 运维复杂度 | 几乎不需要 | 需要装环境、处理依赖和过期软件 |
| 部署速度 | 秒级部署、全边缘 | 需要服务器推代码、重启服务 |
| 全球访问速度 | 边缘节点很近 | 单点,海外可能慢 |
| 重计算能力 | 受 CPU 时间限制 | 可以做批处理、长时间运行 |
| 调试体验 | 日志分散,需要习惯 | 本地登录查看方便 |
| 状态持久化方式 | 分布式存储但各有限制 | 完整的 Linux 文件系统 |
我的结论是:如果你想跑的是"轻量、在线服务型"的个人应用,比如导航页、书签、笔记 API、RSS 订阅、个人短链,cloudflare-os 完胜。但如果你要跑私有的 Git 仓库、需要视频转码、跑 Minecraft 服务器,千万别指望 Worker,老老实实上 VPS。
5.2 后续还能怎么扩展
这个"假 OS"的边界被 Cloudflare 产品线不断推大。我目前已经扩展了四个有意思的模块:
- 用 Cron Triggers 定时抓取几个网站的信息,存进 KV,早上给自己推送一条汇总。
- 用 R2 做了一个个人网盘接口,上传下载不受流量限制,按月存储量计算费用,非常便宜。
- 用 Workers AI 在一个 Worker 里直接调用大模型,给自己做了一个自动打标签的小工具。
- 用 Email Routing 把域名邮箱收件转发到一个 Worker,再把特定关键字邮件存成笔记。
这些模块互相独立,但又可以通过同一个 Worker 转发路由串起来。架构上很干净,每个模块是一个函数,新增 "系统功能" 时不去破坏原有模块。这会让你的项目越堆越大却仍然可控。
说点实在的。我踩过最大的坑不是代码问题,而是惯性思维——总想着要一台"服务器"才能跑应用。cloudflare-os 出现后,大大改变了我对个人项目的部署方式:少买虚拟机、多写函数。虽然它跟传统操作系统完全不搭边,但只要你把它当成"一组可编排的云端资源",就会打开新思路。如果你也准备尝试,建议先从一个很小的功能开始,把书签或短链跑通,再慢慢往里面加模块。等你的个人云服务铺成一片,回头再看老 VPS,是真的不想再回去折腾了。