作为一名长期使用 Obsidian 记录生活和技术的用户,我也同时是静态博客 Hugo 的拥趸。但长久以来,最大的痛点在于如何把这两者优雅地连接起来。
我不希望每次发布文章都要手动复制文件、处理图片路径、调整 Front Matter。我理想中的流是:在 Obsidian 里按下 Ctrl+S,本地的 Hugo 预览就能实时更新,确认无误后一个 Git Push 完成发布。
经过一番折腾,我终于基于 CouchDB 和自己编写的一个同步脚本,打通了这套全链路。
架构概览
我的整套发布流涉及以下几个核心节点:
- 写作端:Obsidian (配合 Self-hosted Livesync 插件)
- 数据中转:CouchDB (运行在 Docker 中)
- 同步核心:自研提取脚本 (运行在 Docker 中)
- 生成端:本地 Hugo 环境
- 图床方案:Lsky Pro + Cloudflare R2
核心流程
- Obsidian 写作:我在 Obsidian 中修改笔记。
- 实时同步:
Obsidian Livesync插件将变更实时推送到我的私有CouchDB数据库。 - 自动提取:我的同步脚本监听数据库变动,解析数据,还原为标准的
.md文件写入 Hugo 的content目录。 - 本地调试:本地运行的
hugo server检测到文件变动,实时刷新浏览器预览。 - 发布:确认无误后,Git Commit & Push,触发 CI/CD 或直接推送到服务器。
为什么不直接用 Obsidian Git?
很多人可能会问:“为什么不直接用 Obsidian Git 插件把 Vault 推送到 GitHub,然后用 GitHub Actions 自动构建 Hugo 呢?”
虽然 Git 方案配置简单,但在实际的高频写作和多端同步场景下,我这套“数据库 + 中转脚本”的方案有着无法替代的优势:
1. 移动端的“无感”体验 (The Mobile Experience)
Git 方案的痛点:在手机(尤其是 iOS)上使用 Git 体验极差。你需要在写作前
Pull,写作后Push。如果忘记 Pull 直接写,很容易产生冲突;如果忘记 Push,回到电脑前又看不到最新的修改。这打断了“随时随地记录”的心流。本方案优势:Livesync 是基于数据库的实时(或准实时)同步。我在 iPhone 上写一段话,几秒钟后它就自动出现在了我的电脑和 CouchDB 里,完全不需要手动操作。这种体验更接近 Notion 或 Google Docs,丝般顺滑。
2. “保存”与“发布”的逻辑解耦
Git 方案的痛点:如果你用 Git 插件自动备份(比如每 10 分钟),你的 Commit 历史会充斥着 “Auto backup” 这样的垃圾信息。如果你手动 Commit,又很容易忘记。更重要的是,保存笔记和发布博客混在了一起,你想保存草稿,却可能意外触发了 CI/CD 自动发布。
本方案优势:
写作层:利用 CouchDB 同步,我可以随意修改、产生无数个中间版本,这只是“数据同步”。
发布层:当我觉得文章写好了,脚本已经把它提取到了 Hugo 目录。此时我只需在 Hugo 目录下做一次有意义的
Git Commit & Push。结论:保持了 Git 历史的干净,且发布动作充满了仪式感和掌控感。
3. 精准的隐私隔离 (Selective Publishing)
Git 方案的痛点:通常 Git 插件是针对整个 Vault 的。如果你想把博客源码开源,却又想保留私人日记,你需要折腾
.gitignore或者复杂的 submodule 嵌套,稍有不慎就会把私钥或日记 push 上去。本方案优势:我的脚本原生支持
TARGET_FOLDER过滤。我的 Vault 里可以有Diary、Work、Finance等无数私密文件夹,脚本只会把Blog文件夹下的内容提取出来。物理级别的隔离,让我对隐私安全绝对放心。
4. 避免 Git 冲突地狱
Git 方案的痛点:Obsidian 的文件本质是文本,但前台配置(workspace, appearance)经常变动。多端 Git 同步时,经常出现
Merge Conflict,导致文件里出现<<<< HEAD这种破坏标记,修复起来很烦人。本方案优势:Livesync 基于 CouchDB 的文档级冲突解决机制,比 Git 处理纯文本冲突更智能、更温和。即使出现冲突,也是在数据库层面解决,不会破坏本地正在调试的 Hugo 构建环境。
5. 纯净的内容交付
Git 方案的痛点:Obsidian 的 Markdown 语法(如双链
[[Link]])和 Hugo 标准 Markdown 并不完全兼容。直接 Push 原文可能导致博客渲染出错。本方案优势:既然中间有一个 Python 脚本在跑,我拥有了预处理能力。未来我可以在脚本里加入正则替换功能,比如自动把
[[Obsidian链接]]转换为[Hugo链接](...),或者自动处理 Front Matter 的格式转换,这是直接 Git Push 做不到的。
6. Self-Hosted 玩家的终极快乐:内网闭环
如果你不是将博客托管在 GitHub Pages,而是运行在自己的服务器(VPS 或 NAS)上,那这个方案简直是为你量身定做的。
Git 方案的痛点:如果你自建博客,使用 Git 发布通常意味着:
本地 Push->GitHub 接收->触发 Webhook->服务器 Pull->构建。或者你需要配置 GitHub Actions Runner,把数据传给微软(GitHub),再传回你的服务器。这不仅慢,而且依赖外部网络。一旦 GitHub 宕机或网络波动,发布就卡住了。本方案优势:这是一条纯内网的、点对点的高速通道。
拓扑优势:Obsidian 在手边,CouchDB 和 Web Server 在自家服务器里。数据从头到尾没有流经任何第三方平台。
架构极简:脚本直接把 Markdown 吐到 Web 服务器(如 Nginx 容器挂载的目录)或 Hugo 的监听目录下。没有 Webhook,没有 CI/CD 流水线,没有公网传输。
极致速度:这种“左手倒右手”的本地文件系统操作,让更新速度只取决于你服务器的磁盘 IO,而不是公网带宽。
关于图片的处理:图床方案
在静态博客中,图片管理一直是个麻烦事。如果把图片和 Markdown 混在一起同步,仓库体积会迅速膨胀,且路径处理极其繁琐。
我的解决方案是完全剥离图片存储:
- 存储后端:使用 Cloudflare R2,免费额度大且速度快。
- 管理前端:自建 Lsky Pro 图床。
- 上传插件:在 Obsidian 中安装 Image To Lskypro 插件。
效果:当我在 Obsidian 中粘贴图片时,插件自动将其上传至 R2,并返回标准的 Markdown 图片链接(如 ![] (https://img.mydomain.com/...))。
因此,存入 CouchDB 和同步到本地的 Markdown 文件中,只包含纯文本链接,干净清爽。
缺失的一环:从 CouchDB 到 Markdown
Obsidian Livesync 插件非常强大,实现了多端实时同步。但它为了效率,将数据以 JSON/Blob 的形式存在 CouchDB 里,并不是直接存储为文件系统中的 .md 文件。
为了把数据库里的“碎片”还原成 Hugo 能识别的博客文章,我需要一个“翻译官”。
自研同步脚本 (Obsidian-Sync-Script)
我在 Docker 容器中跑了一个 Python 脚本,充当了这个桥梁。
它主要做了这几件事:
- ⏱️ 慵懒轮询:脚本会以设定的时间间隔(例如每 20 秒)去询问 CouchDB 是否有新变动。虽然不是毫秒级实时,但作为博客发布流,这种准实时的节奏既节省资源,又能保证写完几口咖啡的时间里内容就已同步。
- 📂 目录还原:完美保留 Obsidian 中的文件夹层级结构(例如
/Blog/Tech/Docker.md)。 - 🧹 自动清理:如果在 Obsidian 里删除了文章或移动了文件夹,脚本会自动同步删除或移动本地文件,保持目录整洁。
- 🛡️ 智能过滤:我设置了
TARGET_FOLDER变量,只同步Blog文件夹下的内容,Obsidian 里的其他私有笔记绝不会泄露到博客目录
设计哲学:为什么选择“慵懒轮询”而非“事件驱动”?
在系统设计中,我们常面临 Push(推/回调)与 Pull(拉/轮询)的抉择。
CouchDB 支持 http_notification,理论上我可以写一个 HTTP Server 来接收变动通知。甚至可以通过防抖(Debounce) 算法来解决“写作时信号过密”的问题。
但经过深思熟虑,我依然坚定地选择了 Pull(轮询) 模式,原因在于架构的极简性:
1. 避免“服务端”的运维成本
如果使用回调,我的脚本必须升级为一个Web 服务器,时刻监听端口等待 CouchDB 的调用。这引入了额外的网络拓扑问题:容器端口映射、防火墙配置、以及潜在的安全风险。 而轮询模式下,脚本仅仅是一个客户端。它在容器内部安安静静地运行,只在需要时主动发起请求。“主动出击”永远比“被动监听”更容易维护。
2. 用最低的成本解决“并发冲突”
写作是一个高频持续的动作。使用回调模式,面对短时间内涌入的数十次请求,我需要编写复杂的多线程锁(Lock)或任务队列机制,来防止多个请求同时写入磁盘造成文件损坏。 而慵懒轮询天然就是一种单线程的节流机制。While True 循环保证了同一时间只有一个同步任务在执行,上一次没同步完,绝不会开始下一次。这种串行化的特性,用最笨的代码换来了最高的稳定性。
开源与使用
为了方便维护和部署,我将这个脚本封装成了 Docker 镜像。如果你也在使用 Obsidian Livesync + CouchDB 方案,并且想实现类似的自动化导出功能,可以直接使用我的镜像。
Github 项目地址:
👉 Obsidian-CouchDB-Sync-Script
(具体的 Docker Compose 配置和环境变量说明,请移步 GitHub 仓库的 README 查看)
结语
折腾工具的最终目的是为了忘记工具。
现在,无论是用电脑还是手机,只要我在 Obsidian 的 Blog 目录下新建一篇笔记,它就会自动出现在我的 Hugo 目录中。这种无感知的写作体验,让写博客重新变成了一件纯粹快乐的事情。