移植 Memos 笔记到 Cloudflare Workers 上运行

移植 Memos 笔记到 Cloudflare Workers 上运行
MoshineMemos 是一款非常轻量的自托管的备忘录中心。你可以把它当作个人笔记本、备忘录,多账号功能,实现一个轻量级的个人博客说说栏。之前比较喜欢用手机自带的备忘录或者电脑的记事本,但是这样有两个问题:
- 不方便分享: 如果我分享给小伙伴,那么通常是需要截屏分享。
- 数据共享不方便: 电脑和手机使用各自备忘录,但是需要数据互传时就非常不太方便,需要通过其他平台互传。
Memos 的一个好处,就是支持多平台,比如移动端你可以用 MoeMemos(Android 或者 iOS),桌面端你可以用 Memos 自带的 Web:

而且,Memos 还支持多账号,这样我们就可以和小伙伴一起使用 Memos,共同维护一个 Memos 服务,实现一个轻量级的说说栏。
Memos 的官方版本需要通过 Docker 部署到 VPS 或本地使用,这无形中提高了使用成本。本文详细介绍如何将 Memos 笔记应用完整迁移到 Cloudflare 边缘平台,使用 Workers + D1 + R2 替代原有的 Go + SQLite + 本地存储架构。
项目仓库地址:https://github.com/jkjoy/memos-on-cloudflare
功能特性
- Markdown 备忘录(支持标签、代码块、任务列表、Mermaid 图表)
- 多用户支持(管理员/普通用户)
- 备忘录可见性(私有/工作区/公开)
- 文件附件上传(最大 100MB)
- 备忘录分享链接(可设过期时间)
- 备忘录评论和表情反应
- 音频录制 + AI 转写
- SSO 单点登录
- 多语言支持(中文、英文等 30+ 语言)
- 暗色/亮色主题
- 日历热力图
- 标签管理
- Webhook 通知
与原版 Memos 的区别
| 项目 | 原版 Memos | 本项目 |
|---|---|---|
| 后端 | Go + gRPC | Cloudflare Workers + Hono |
| 数据库 | SQLite (本地文件) | Cloudflare D1 (托管 SQLite) |
| 文件存储 | 本地/S3 | Cloudflare R2 |
| AI | OpenAI/Gemini API | Cloudflare Workers AI |
| 部署 | Docker/二进制 | wrangler deploy |
| 运维 | 需要服务器 | 无服务器,零运维 |
| 前端通信 | Connect RPC (protobuf) | REST JSON |
技术栈
| 层级 | 技术 |
|---|---|
| 运行时 | Cloudflare Workers |
| 后端框架 | Hono |
| 数据库 | Cloudflare D1 (SQLite) |
| 文件存储 | Cloudflare R2 |
| AI | Cloudflare Workers AI (Whisper) |
| 前端 | React + Vite + TailwindCSS |
| 认证 | JWT (HS256) + bcrypt |
前置要求
- Node.js >= 18
- Wrangler CLI >= 4.14
- Cloudflare 账号(已开通 Workers、D1、R2)
快速部署
1. 克隆仓库
1 | git clone https://github.com/jkjoy/memos-on-cloudflare.git |
进入项目文件夹
1 | cd memos-on-cloudflare |
2. 安装依赖
1 | npm install |
安装 Web 所需依赖
1 | cd web && npm install && cd .. |
3. 创建 Cloudflare 资源
创建 D1 数据库
1 | wrangler d1 create cfmemos-db |
创建 R2 存储桶
1 | wrangler r2 bucket create cfmemos |
创建 KV 存储空间
1 | wrangler kv namespace create cfmemos-cache |
[!caution]
务必记录下 创建 D1 数据库 及 创建 KV 存储空间 时返回的 ID 字符串,编辑wrangler.toml时需要使用。
4. 配置 wrangler.toml
将第 3 步创建 D1 时返回的 database_id 和 kv_namespaces id 填入 wrangler.toml:
1 | name = "cfmemos" |
[!important]
如果要实现手机端,如:MoeMemosAndroid 和网页版 Memos on Cloudflare 同步,需要将 wrangler.toml 文件中的APP_VERSION值和 version.ts 文件中的DEFAULT_APP_VERSION值设定为Android手机端 APP 支持的 Memos versions 号。
5. 设置生产密钥
设置 JWT 密钥(务必使用强随机字符串)
1 | wrangler secret put JWT_SECRET |
6. 初始化数据库
1 | npm run db:migrate:remote |
7. 构建并部署
1 | npm run deploy |
部署完成后,访问 Workers 分配的域名,首次访问会进入管理员注册页面。注册的第一个用户为管理员账户。
8. 在 D1 数据库中激活 AI 引擎(可选择,实测转录效果很差)
部署完成后,进入 Cloudflare Dashboard → D1 → cfmemos-db → Console,运行以下 SQL 将 AI 提供商写入系统数据库(适配前端兼容性):
1 | INSERT INTO system_setting (name, value) |
[!caution]
切勿在 Memos 网页端的“系统设置 → AI”页面点击“保存”!前端 UI 无法原生存取 WORKERS_AI 类型,网页端点击保存会将数据库中的 providers 再次覆写为空数组 []。后续如需更新配置,请始终通过 D1 SQL Console 运行上述语句。
GitHub Actions 自动部署
推送到 main 分支会自动触发部署。需要在 GitHub 仓库 Settings → Secrets and variables → Actions 中添加:
| Secret | 说明 |
|---|---|
CLOUDFLARE_API_TOKEN |
Cloudflare API Token(需要 Workers Scripts:Edit、D1:Edit、R2:Edit 权限) |
CLOUDFLARE_ACCOUNT_ID |
Cloudflare Account ID(在 Dashboard 右侧栏可找到) |
工作流会自动完成:安装依赖 → 构建前端 → 执行数据库迁移 → 部署 Worker。
本地开发
需要两个终端窗口:
1 | # 终端 1:启动 Worker 后端(端口 8787) |
浏览器访问 http://localhost:3001。
项目结构
1 | ├── wrangler.toml # Cloudflare 配置(D1、R2、AI 绑定) |
环境变量
| 变量 | 说明 | 必填 |
|---|---|---|
JWT_SECRET |
JWT 签名密钥,生产环境必须使用强随机字符串 | 是 |
INSTANCE_NAME |
实例名称,显示在页面标题 | 否 |
生产环境通过 wrangler secret put 设置敏感变量,非敏感变量在 wrangler.toml 的 [vars] 中配置。
Cloudflare 资源绑定
| 绑定名 | 类型 | 用途 |
|---|---|---|
DB |
D1 Database | 存储用户、备忘录、设置等所有结构化数据 |
BUCKET |
R2 Bucket | 存储附件文件(图片、音频、文档) |
AI |
Workers AI | 音频转写(@cf/openai/whisper) |
ASSETS |
Static Assets | 托管前端构建产物 |
自定义域名
在 Cloudflare Dashboard 中为 Worker 添加自定义域名:
- Workers & Pages → cfmemos → Settings → Domains & Routes
- 添加自定义域名(需要域名已在 Cloudflare DNS 中)
常见问题
Q: 部署后访问显示空白页?
确认 npm run build:web 已执行且 web/dist/ 目录存在。wrangler deploy 会自动上传该目录。
Q: 访问 /api/* 返回前端 404 页面?
确认 wrangler.toml 的 [assets] 配置包含:
1 | run_worker_first = ["/api/*", "/file/*", "/u/*"] |
否则 Cloudflare 静态资源层可能会先处理请求,并把不存在的 API 路径回退到 SPA 的 index.html,最终显示前端 404 页面,而不是进入 Worker API 路由。
Q: 数据库报错 “table not found”?
执行 npm run db:migrate:remote 初始化远程数据库 schema。
Q: 如何备份数据?
1 | # 导出 D1 数据库 |
Q: 上传大小限制?
附件上传硬编码为 100MB。Workers 免费版单次请求体限制为 100MB,付费版无此限制。
Q: 免费额度够用吗?
Cloudflare Workers Free Plan 包含:每天 10 万次请求、D1 5GB 存储、R2 10GB 存储 + 每月 1000 万次读取。个人使用完全足够。
Q: 录音按钮不存在,或者录音条旁边的“转录”图标丢失?
这是 AI 语音转写功能中最常见的问题,通常由以下 4 层原因导致,请按顺序排查:
第一层:浏览器“跟踪防护”拦截(概率最高)
- 现象:点击
+找不到录音按钮,或无法获取麦克风。 - 解决:Edge/Chrome 的“严格跟踪防护”会屏蔽
navigator.mediaDevices接口。请点击浏览器地址栏左侧 🔒 锁头图标,关闭 “此网站的跟踪防护”,并将 麦克风权限 设置为允许,按Ctrl + F5强制刷新。
- 现象:点击
第二层:Cloudflare KV 缓存死锁(后端改配置不生效)
- 现象:SQL 写入或环境变量配置后,前端依然提示无 AI 服务。
- 原因:Worker 会把配置缓存在 KV(
cfmemos-cache)中。修改 D1 或环境变量不会自动刷新 KV。 - 解决:进入 Cloudflare Dashboard → KV → cfmemos-cache → KV 对,点击 删除全部/批量删除 强行清空缓存。
第三层:
wrangler deploy冲掉 Dashboard 环境变量- 现象:重新部署代码后,语音转写功能突然失效。
- 原因:命令行部署会覆盖 Dashboard 手动添加的变量。
- 解决:确认
CLOUDFLARE_ACCOUNT_ID和CLOUDFLARE_API_TOKEN已写入wrangler.toml的[vars]节点下。
第四层:网页前端 LocalStorage 缓存锁死
- 现象:后端
GET /api/v1/instance/settings/AI已有正确 JSON 数据,但 UI 仍不渲染图标。 - 解决:按
F12打开开发者工具 → 切换到 应用 (Application) → 点击 清除网站数据 (Clear site data),刷新网页并重新登录账号。
- 现象:后端
Q: AI 语音转写成功触发,但提示超时或无法识别?
- 控制录音时长:Workers AI 免费版对内存及 CPU 处理时长有严格限制,建议录音控制在 2~30 秒 的短语音。
- 激活 Workers AI 服务:登录 Cloudflare Dashboard,确保已进入过 AI → Workers AI 页面并同意了服务条款,否则 API 调用会被拒绝。








