ℹ️ 读者定位
适合你,如果:你有一台支持 Docker Compose 的服务器(也可以是Nas),正在使用 Obsidian,希望把多端同步服务和网页只读浏览都放在自己家里。
开始前需要:云服务器管理权限、SSH 或容器管理器、基础命令行能力,以及一份已经验证可恢复的 Obsidian 备份。
读完可以完成:跑通 Fast Note Sync 服务端、把指定 Vault 同步到 云服务器 本地目录,再通过 Perlite 从浏览器只读查看。
暂时不适合:完全不熟悉 Docker、还没有做任何备份,或者准备把9000、8090端口直接裸露到公网的人。ARM64 云服务器 还要先解决 Perlite 官方镜像兼容性。
📄 这篇文章最终要跑通什么
数据链路是:Obsidian → Fast Note Sync Service → FastNodeSync-CLI → NAS Vault → Perlite → Nginx。
部署顺序是:先启动 FNS,创建 Vault 和 Token;再让 CLI 首次只拉取;确认文件正确后,最后开启常驻双向同步和网页发布。
这篇内容分为以下几个部分:
- 先讲清四个服务分别做什么。
- 修正原始 Compose 的结构问题。
- 按三阶段顺序部署。
- 连接 Obsidian,并完成三层验收。
- 处理 ARM 架构、安全、备份和升级问题。
一、先看懂这套部署
1.1. Fast Note Sync 解决什么问题
Fast Note Sync 不是简单的网盘文件夹同步。它由服务端和客户端协作,通过 WebSocket 把笔记、附件、目录变化以及可选的 Obsidian 配置同步到多个设备。
在这套方案里,FNS 服务端负责接收和分发变化;Obsidian 插件负责电脑、手机端同步;FastNodeSync-CLI 负责把服务端 Vault 落到 云服务器 的普通文件夹。
这里一定要注意:FNS 服务端自己的storage不是给 Perlite 直接读取的标准 Obsidian Vault。所以中间需要 CLI,把内容还原成普通 Markdown 目录。
1.2. 四个服务分别做什么
| 服务 | 核心职责 | 宿主机端口 | 关键挂载 | 权限 |
|---|---|---|---|---|
fast-note-sync-service | Web 管理、REST、WebSocket、同步数据保存 | 9000 | storage、服务端config | 读写 |
fns-cli | 把远端 Vault 同步成 云服务器 本地文件夹 | 无 | CLIconfig、vault | 配置只读、Vault 读写 |
perlite | 把 Markdown Vault 解析成网页 | 无 | 同一个vault | 只读 |
perlite_web | 用 Nginx 暴露 Perlite 页面 | 8090 | perlite.conf | 只读 |
perlite和perlite_web是两个容器,是因为 Perlite 镜像运行 PHP-FPM,而 Nginx 负责接收浏览器请求。
NAS 中四个服务的职责与共享 Vault
1.3. 最终数据流
Fast Note Sync 在 NAS 上的端到端架构
从图里可以看到两种完全不同的数据方向:
- Obsidian、FNS 和 CLI 之间是双向同步。
- 云服务器 Vault 到 Perlite 是只读展示。
⚠️ 同步不是备份
双向同步会快速传播正常修改,也会传播误删除和错误修改。部署同步之前先做备份,部署成功后仍然要保留独立快照和离线副本。
二、先修正原始 Compose
2.1. 原始文件有哪些问题
你提供的思路是对的,但 YAML 不能直接运行:
| 问题 | 会造成什么结果 | 修正方式 |
|---|---|---|
顶层services:重复三次 | 服务被错误嵌套,Compose 无法解析 | 只保留一个顶层services: |
| 服务端第二条 volume 没有缩进 | config挂载不属于服务 | 对齐到volumes:列表 |
depends_on列表缩进错误 | Nginx 依赖关系无效 | - perlite:ro放到depends_on下 |
服务端和 CLI 共用./config | 两套同名、不同结构的配置互相污染 | 拆成fast-note-sync/config和fns-cli/config |
fns-cli直接写build: . | Compose 不在源码根目录时找不到 Dockerfile | 明确build.context: ./fns-cli |
| CLI 与 Perlite 使用不同宿主机路径 | 两者看到的可能不是同一个 Vault | 统一使用./fns-cli/vault |
| 第一次直接启动全部服务 | CLI 没有 Token,会反复认证失败 | 分阶段启动 |
version: "3" | Compose V2 会提示该字段已经过时 | 当前示例直接省略 |
先执行下面的命令,是排查 Compose 最省时间的一步:
dockercompose config只要这里报错,就不要继续up -d。
2.2. 推荐目录结构
本文假设 Compose 放在:
/home/caimingyang/obsidian/最终目录如下:
/home/caimingyang/obsidian/ ├── docker-compose.yml ├── fast-note-sync/ │ ├── config/ │ └── storage/ ├── fns-cli/ │ ├── Dockerfile │ ├── requirements.txt │ ├── fns_cli/ │ ├── config/ │ │ └── config.yaml │ └── vault/ └── perlite/ └── perlite.conf这样处理以后:
- 服务端数据在
fast-note-sync/。 - CLI 源码、配置和 Vault 在
fns-cli/。 - CLI 读写
fns-cli/vault。 - Perlite 只读同一个
fns-cli/vault。
2.3. 修正后的完整 Compose
可直接复制的文件已经保存到:
01-素材/部署示例/docker-compose.yml完整内容如下:
services: fast-note-sync-service: image: haierkeys/fast-note-sync-service:latest container_name: fast-note-sync-service restart: always ports: -"9000:9000"volumes: - ./fast-note-sync/storage:/fast-note-sync/storage - ./fast-note-sync/config:/fast-note-sync/config fns-cli: build: context: ./fns-cli dockerfile: Dockerfile container_name: fns-cli restart: unless-stopped environment: -PYTHONUNBUFFERED=1volumes: - ./fns-cli/config:/app/config:ro - ./fns-cli/vault:/app/vault:rw depends_on: - fast-note-sync-service perlite: image: sec77/perlite:latest container_name: perlite restart: unless-stopped environment: NOTES_PATH:"Notes"HIDE_FOLDERS:".obsidian,.git,private,trash,templates"HIDDEN_FILE_ACCESS:"false"LINE_BREAKS:"true"ABSOLUTE_PATHS:"false"NICE_LINKS:"true"SHOW_TOC:"true"SHOW_LOCAL_GRAPH:"false"DISABLE_POP_HOVER:"true"HTML_SAFE_MODE:"true"ZETTELKASTEN_FILENAMES_ENABLED:"false"HOME_FILE:"README"FONT_SIZE:"16"SITE_TITLE:"我的笔记"SITE_NAME:"Obsidian 知识库"SITE_TYPE:"article"ALLOWED_FILE_LINK_TYPES:"pdf,mp4,webm,mp3,m4a,doc,docx,xls,xlsx,zip,png,jpg,jpeg,gif,webp,svg"HIGHLIGHTJS_LANGS:"java,javascript,typescript,sql,bash,powershell,yaml,json,xml,python,go,c,cpp"volumes: - ./fns-cli/vault:/var/www/perlite/Notes:ro perlite_web: image: nginx:stable container_name: perlite_web restart: unless-stopped ports: -"8090:80"volumes: - ./perlite/perlite.conf:/etc/nginx/conf.d/default.conf:ro volumes_from: - perlite:ro depends_on: - perlite networks: default: name: obsidian-fns💡 为什么 CLI 使用内部服务名
fns-cli与 FNS 在同一个 Compose 网络里,所以 API 地址直接写http://fast-note-sync-service:9000。不要让容器绕到公网域名再访问同一台 云服务器。
三、准备 云服务器 环境
3.1. 先检查四件事
登录 云服务器 SSH 后执行:
uname-mdockerversiondockercompose versiondockercomposels然后确认:
9000没有被其他服务占用。8090没有被其他网页服务占用。- 云服务器 存储空间足够容纳服务端数据和一份本地 Vault。
- 当前 Obsidian Vault 已有独立备份或 NAS 快照。
🚨 ARM64 NAS 先不要直接启动 Perlite
截至 2026-07-17,Perlite 官方 Docker Hub 的latest/1.6.1只列出linux/amd64和linux/arm/v7,没有列出linux/arm64。如果uname -m返回aarch64,先只部署 FNS 与 CLI;Perlite 需要自行构建或等待、选择经过验证的 ARM64 镜像。
可以用下面的命令再次核对镜像平台:
dockerbuildx imagetools inspect sec77/perlite:latest3.2. 推荐的三阶段部署顺序
NAS 部署 Fast Note Sync 的三阶段流程
为什么不建议一次执行docker compose up -d?
因为 CLI 必须先拿到管理员创建的 Vault 和 JWT Token。第一次同步时,也应该先做一次pull,确认目标目录正确,再开启常驻双向同步。
3.3. 建立目录并获取 CLI 源码
cd/home/caimingyang/obsidiangitclone https://github.com/Go1c/FastNodeSync-CLI.git fns-climkdir-pfast-note-sync/configmkdir-pfast-note-sync/storagemkdir-pfns-cli/configmkdir-pfns-cli/vaultmkdir-pperlite如果fns-cli已经存在,不要重复克隆:
git-C/home/caimingyang/obsidian/fns-cli statusgit-C/home/caimingyang/obsidian/fns-cli pull --ff-only⚠️ 生产环境建议固定 CLI 提交
FastNodeSync-CLI 是 FNS 主仓库列出的第三方客户端,目前没有正式 Release。测试稳定后记录git rev-parse HEAD,升级时先备份再切换提交,不要无条件跟随main。
3.4. 放置 Compose 与 Nginx 配置
需要准备三个文件:
| 文件 | 云服务器 目标位置 |
|---|---|
docker-compose.yml | /home/caimingyang/obsidian/docker-compose.yml |
fns-cli-config.yaml.example | 稍后复制为fns-cli/config/config.yaml |
perlite.conf | /home/caimingyang/obsidian/perlite/perlite.conf |
本文附带的三个示例位于:
01-素材/部署示例/文件放好后,先验证:
cd/home/caimingyang/obsidiandockercompose config✅ Compose 通过的标准
命令能完整展开fast-note-sync-service、fns-cli、perlite、perlite_web四个服务,没有 YAML、缩进、网络或卷定义错误。
四、第一阶段:只启动 FNS 服务
4.1. 启动并检查日志
cd/home/caimingyang/obsidiandockercompose pull fast-note-sync-servicedockercompose up-dfast-note-sync-servicedockercomposepsfast-note-sync-servicedockercompose logs--tail=200fast-note-sync-service浏览器打开:
http://云服务器_IP:9000如果页面打不开,先在 云服务器 局域网内排查,不要第一时间做公网穿透:
curl-Ihttp://127.0.0.1:9000dockercompose logs--tail=200fast-note-sync-service4.2. 初始化管理端
第一次进入管理端时:
- 注册第一个管理员账户。
- 登录管理端。
- 创建或确认要同步的 Vault。
- 记住 Vault 名称,后面三处必须完全一致。
这三处分别是:
- FNS Web 管理端的 Vault 名称。
fns-cli/config/config.yaml中的server.vault。- Obsidian Fast Note Sync 插件授权的 Vault。
05-FNS管理端初始化
4.3. 创建 CLI Token
进入管理端的 Token 管理页面,为 NAS 单独创建一个 Token。
按当前 FastNodeSync-CLI 文档,常驻读写同步可使用类似权限:
p:ws c:fns-cli* f:note_rw,file_rw,config_rw如果暂时不启用.obsidian配置同步,可以先不授予config_rw,并保持:
sync_config:false🚨 Token 就是知识库读写密码
不要把真实 Token 写进文章、截图、Git 仓库或群聊。为 云服务器 单独生成 Token,设置合理有效期;泄漏后立即吊销并重新生成。
06-FNS令牌创建
4.4. 初始化后关闭公开注册
官方配置支持:
user: register-is-enable:false完成首个账户初始化后,检查挂载目录中的服务端config.yaml,关闭公开注册,再重启服务:
dockercompose restart fast-note-sync-servicedockercompose logs--tail=100fast-note-sync-service如果只在可信局域网使用,也建议关闭;如果准备接入公网,更不能长期开放匿名注册。
五、第二阶段:配置 CLI 并首次只拉取
5.1. 编写 CLI 配置
创建:
/home/caimingyang/obsidian/fns-cli/config/config.yaml内容如下:
server: api:"http://fast-note-sync-service:9000"token:"请替换为在 FNS Web 管理端生成的 JWT Token"vault:"知识库"sync: watch_path:"/app/vault"sync_notes:truesync_files:truesync_config:falseexclude_patterns: -".git/**"-".trash/**"-"*.tmp"-".fns_state.json"file_chunk_size:524288client: reconnect_max_retries:15reconnect_base_delay:3heartbeat_interval:30client_type:"fns-cli"logging: level:"INFO"file:""需要替换的只有两个核心值:
token:刚才创建的 JWT Token。vault:与 FNS 和 Obsidian 完全一致的 Vault 名称。
5.2. 保护配置文件
chmod600/home/caimingyang/obsidian/fns-cli/config/config.yamlCompose 中把配置目录挂载为只读:
- ./fns-cli/config:/app/config:ro但要明白:容器只读不等于宿主机安全。NAS 管理员、备份系统和有权限读取该目录的进程仍能看到 Token。
5.3. 构建 CLI 并首次只拉取
先构建,不启动常驻服务:
cd/home/caimingyang/obsidiandockercompose build fns-cli第一次建议对一个空的fns-cli/vault执行pull:
dockercompose run--rmfns-cli pull-c/app/config/config.yaml为什么强调pull?因为它先把服务端内容落到 云服务器,避免一上来就监控一个目录状态不明确的本地 Vault 并向远端推送。
⚠️ 第一次同步前再确认一次
fns-cli/vault应该是空目录,或者是你已经确认与服务端完全一致的副本。不要拿一个来源不明、内容不同的旧 Vault 直接启动双向同步。
5.4. 检查第一次拉取结果
至少检查:
find/home/caimingyang/obsidian/fns-cli/vault-maxdepth2-typef|head-n50应该重点确认:
- 中文文件名是否正常。
- Markdown 文件数量是否合理。
- 图片和其他附件是否存在。
.fns_state.json是否生成。- Vault 根目录是否存在
README.md。
README.md是 Perlite 的首页。如果远端没有,建议先在 Obsidian 中创建,再同步下来,不要随便在 云服务器 端创建一个可能覆盖远端的同名文件。
✅ 首次拉取通过的标准
CLI 命令正常退出;云服务器 Vault 中出现预期的笔记和附件;抽查几个中文文件可以正常打开;没有认证、Vault 不存在或权限不足错误。
六、第三阶段:开启同步和网页发布
6.1. 启动常驻服务
确认首次拉取没有问题后再启动:
dockercompose up-dfns-cli perlite perlite_webdockercomposeps检查日志:
dockercompose logs--tail=200fns-clidockercompose logs--tail=100perlitedockercompose logs--tail=100perlite_web容器显示Up只是第一层。还要观察几分钟,确认fns-cli没有因为 Token、Vault 或网络问题反复重启。
6.2. 安装并授权 Obsidian 插件
在 Obsidian 中:
- 打开“设置 → 第三方插件 → 浏览”。
- 搜索并安装
Fast Note Sync。 - 启用插件。
- 回到 FNS Web 管理端。
- 打开“笔记仓库/Vault”页面。
- 点击“一键授权 Obsidian”;或者手动复制 API 配置到插件。
⚠️ 手机端一键唤起失败怎么办
先确认手机能访问 FNS 地址,再尝试手动粘贴配置。局域网地址、HTTPS 证书、反向代理和浏览器是否允许唤起 Obsidian,都会影响一键授权。
08-Obsidian一键授权
6.3. 打开 Perlite
浏览器访问:
http://云服务器_IP:8090如果首页空白,优先检查:
fns-cli/vault/README.md是否存在。NOTES_PATH是否等于容器内目录名Notes。- Perlite 是否挂载到
/var/www/perlite/Notes:ro。 - Nginx 配置中的
fastcgi_pass是否为perlite:9000。 perlite_web是否通过volumes_from: perlite:ro读到相同文件。
09-Perlite网页效果
七、不要只看容器状态,要做三层验收
7.1. 验收清单
| 层级 | 测试动作 | 成功标准 |
|---|---|---|
| 服务层 | 打开9000、检查 FNS 日志 | 管理端可登录,日志无持续报错 |
| 同步层 | Obsidian 新建含图片的测试笔记 | 云服务器 Vault 出现 Markdown 和图片 |
| 反向同步 | 修改一篇专用测试笔记 | 另一端收到修改,不产生重复文件 |
| 展示层 | 打开8090 | Perlite 能显示目录、正文、图片和链接 |
| 持久化 | 重启容器或 NAS | 账户、Token、Vault、页面仍存在 |
| 恢复层 | 从备份恢复一份测试数据 | 可以打开并核对,不只是“有备份文件” |
7.2. 做一次端到端测试
建议在 Obsidian 创建:
FNS-部署验收.md测试内容至少包含:
- 中文文件名。
- 一张图片。
- 一个
[[双向链接]]。 - 一个标签。
- 一个代码块。
然后按顺序检查:
- 电脑端保存测试笔记。
- 手机端确认收到。
- 云服务器
fns-cli/vault确认文件和图片出现。 - Perlite 确认页面能够打开。
- 在另一台 Obsidian 设备修改测试文字。
- 检查其他端是否同步更新。
7.3. 做一次重启测试
dockercompose restartdockercomposepsdockercompose logs--tail=100fast-note-sync-servicedockercompose logs--tail=100fns-cli最后再重启一次 云服务器,确认:
- FNS 账户和 Vault 没有丢失。
- CLI 能根据
.fns_state.json继续增量同步。 - Perlite 仍然读取同一个 Vault。
- 容器没有因为目录权限变化而启动失败。
八、同步、展示、备份要分开管理
8.1. 三条边界
同步、展示和备份的职责边界
这张图里最重要的不是三个组件,而是三句话:
- 同步会传播错误。
- 只读挂载不等于访问控制。
- 有备份文件不等于能够恢复。
8.2. 至少备份哪些目录
| 目录 | 内容 | 建议 |
|---|---|---|
fast-note-sync/storage | 默认数据库、附件和服务端数据 | 定期快照;一致性备份时暂停写入或使用 云服务器 一致性快照 |
fast-note-sync/config | 服务端配置 | 每次改配置后备份 |
fns-cli/config | CLI 配置与 Token | 加密备份、严格限制权限 |
fns-cli/vault | 可直接阅读的 Obsidian 文件 | 快照 + 离线副本 + 可选 Git 版本 |
docker-compose.yml、perlite.conf | 部署定义 | 放入私有配置仓库,但不要提交真实 Token |
🚨 不要只复制正在写入的数据库文件
FNS 默认可使用 SQLite。备份storage时,优先停止服务、使用 云服务器 的一致性快照,或者采用数据库支持的一致性备份方式。复制到一半的数据库不一定能恢复。
8.3. 更新与回滚
测试阶段可以使用latest,稳定运行后建议固定已验证版本或镜像摘要。
更新前:
dockercompose imagesgit-Cfns-cli rev-parse HEAD然后:
- 记录当前镜像版本和 CLI commit。
- 创建
storage、config、vault快照。 - 拉取新镜像或更新 CLI。
- 重新构建并启动。
- 完成同步、附件、Perlite 和重启验证。
dockercompose pull fast-note-sync-service perlite perlite_webdockercompose build --no-cache fns-clidockercompose up-ddockercomposeps⚠️ 不要把“容器启动成功”当成“升级成功”
升级后必须再做一遍测试笔记、附件、Token 权限、Perlite 页面和重启验证。出现问题时,回到刚才记录的镜像版本、CLI commit 和数据快照。
8.4. 公网访问和 Token
最安全的顺序是:
- 先只在可信局域网跑通。
- 需要远程访问时优先使用 VPN。
- 确实需要公网时,再部署 HTTPS 反向代理和身份认证。
FNS 反向代理要支持 WebSocket;Perlite 前面要有访问控制。HIDE_FOLDERS只是页面展示过滤,不是权限系统。
🚨 不要把两个端口直接映射到公网
9000包含管理端和同步接口,8090默认也没有为你的私人知识库提供完整身份认证。至少使用 HTTPS、访问控制、强密码、独立 Token 和最小权限。
九、常见问题与避坑
9.1.services must be a mapping
❓ 为什么 Compose 一运行就报 YAML 错误?
原因通常是重复services:或列表缩进错误。先执行docker compose config,不要凭肉眼继续试。
9.2.unable to prepare context或找不到 Dockerfile
❓ 为什么 fns-cli 构建失败?
build.context必须指向 FastNodeSync-CLI 源码目录,该目录里要有Dockerfile、requirements.txt和fns_cli/。本文使用context: ./fns-cli。
9.3. CLI 找不到配置
❓ 为什么提示 /app/config/config.yaml 不存在?
检查宿主机是否真的存在fns-cli/config/config.yaml,再用docker compose config检查挂载。不要把服务端的config.yaml放进 CLI 配置目录。
9.4. Token 无效或权限不足
❓ 为什么日志出现 401、403 或 WebSocket 认证失败?
检查 Token 是否完整、是否过期、客户端类型是否匹配、权限是否包含需要同步的 note/file/config。不要在日志或截图中暴露 Token。
9.5. Vault 不存在或同步到错误目录
❓ 为什么 CLI 能连接但找不到笔记?
FNS 管理端、CLIserver.vault和 Obsidian 插件中的 Vault 名称必须完全一致,包括大小写和空格。
9.6. Perlite 首页空白或 404
❓ 为什么 8090 能打开,但没有正文?
依次检查README.md、NOTES_PATH=Notes、/var/www/perlite/Notes:ro、fastcgi_pass perlite:9000和volumes_from: perlite:ro。
9.7.no matching manifest for linux/arm64
❌ Perlite 官方镜像与 ARM64 不匹配
当前官方镜像没有列出linux/arm64。先移除或暂停perlite、perlite_web,只跑 FNS 与 CLI。不要用platform: linux/amd64强行模拟后就默认性能和稳定性没有问题。
9.8. 两台设备同时编辑发生覆盖
⚠️ CLI 上游说明并发修改以服务端最后写入为准
重要长文不要在多台离线设备上同时编辑。先用测试笔记模拟一次离线冲突,确认你能接受实际结果,再投入日常使用。
9.9. Perlite 隐藏目录后是不是就安全了
🚨 不是
HIDE_FOLDERS负责“页面不展示”,不是用户身份认证。最稳妥的做法是只给 Perlite 挂载专门的发布 Vault,或者在前面增加 VPN、Basic Auth、Authentik 等访问控制。
十、常用命令和最后结论
10.1. 常用命令
# 检查 Composedockercompose config# 查看状态dockercomposeps# 查看关键日志dockercompose logs-ffast-note-sync-servicedockercompose logs-ffns-cli# 首次只拉取dockercompose run--rmfns-cli pull-c/app/config/config.yaml# 启动常驻同步和发布dockercompose up-dfns-cli perlite perlite_web# 重启dockercompose restart# 查看镜像dockercompose images10.2. 最后记住
这套方案真正稳定的关键,不是把四个容器一次性拉起来,而是守住顺序和边界:
先备份,再启动 FNS; 先创建 Vault 和 Token,再配置 CLI; 先只拉取并检查,再开启双向同步; 同步负责传播,Perlite 负责展示,独立备份负责兜底。参考资料
- Fast Note Sync Service
- Fast Note Sync Obsidian 插件
- FastNodeSync-CLI
- Fast Note Sync Service Docker Hub
- Perlite
- Perlite Docker Setup
- Perlite Docker Hub Tags