ℹ️ 读者定位适合你如果你有一台支持 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-serviceWeb 管理、REST、WebSocket、同步数据保存9000storage、服务端config读写fns-cli把远端 Vault 同步成 云服务器 本地文件夹无CLIconfig、vault配置只读、Vault 读写perlite把 Markdown Vault 解析成网页无同一个vault只读perlite_web用 Nginx 暴露 Perlite 页面8090perlite.conf只读perlite和perlite_web是两个容器是因为 Perlite 镜像运行 PHP-FPM而 Nginx 负责接收浏览器请求。NAS 中四个服务的职责与共享 Vault1.3. 最终数据流Fast Note Sync 在 NAS 上的端到端架构从图里可以看到两种完全不同的数据方向Obsidian、FNS 和 CLI 之间是双向同步。云服务器 Vault 到 Perlite 是只读展示。⚠️ 同步不是备份双向同步会快速传播正常修改也会传播误删除和错误修改。部署同步之前先做备份部署成功后仍然要保留独立快照和离线副本。二、先修正原始 Compose2.1. 原始文件有哪些问题你提供的思路是对的但 YAML 不能直接运行问题会造成什么结果修正方式顶层services:重复三次服务被错误嵌套Compose 无法解析只保留一个顶层services:服务端第二条 volume 没有缩进config挂载不属于服务对齐到volumes:列表depends_on列表缩进错误Nginx 依赖关系无效- perlite:ro放到depends_on下服务端和 CLI 共用./config两套同名、不同结构的配置互相污染拆成fast-note-sync/config和fns-cli/configfns-cli直接写build: .Compose 不在源码根目录时找不到 Dockerfile明确build.context: ./fns-cliCLI 与 Perlite 使用不同宿主机路径两者看到的可能不是同一个 Vault统一使用./fns-cli/vault第一次直接启动全部服务CLI 没有 Token会反复认证失败分阶段启动version: 3Compose 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:9000volumes: - ./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: -PYTHONUNBUFFERED1volumes: - ./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:NotesHIDE_FOLDERS:.obsidian,.git,private,trash,templatesHIDDEN_FILE_ACCESS:falseLINE_BREAKS:trueABSOLUTE_PATHS:falseNICE_LINKS:trueSHOW_TOC:trueSHOW_LOCAL_GRAPH:falseDISABLE_POP_HOVER:trueHTML_SAFE_MODE:trueZETTELKASTEN_FILENAMES_ENABLED:falseHOME_FILE:READMEFONT_SIZE:16SITE_TITLE:我的笔记SITE_NAME:Obsidian 知识库SITE_TYPE:articleALLOWED_FILE_LINK_TYPES:pdf,mp4,webm,mp3,m4a,doc,docx,xls,xlsx,zip,png,jpg,jpeg,gif,webp,svgHIGHLIGHTJS_LANGS:java,javascript,typescript,sql,bash,powershell,yaml,json,xml,python,go,c,cppvolumes: - ./fns-cli/vault:/var/www/perlite/Notes:ro perlite_web: image: nginx:stable container_name: perlite_web restart: unless-stopped ports: -8090:80volumes: - ./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-17Perlite 官方 Docker Hub 的latest/1.6.1只列出linux/amd64和linux/arm/v7没有列出linux/arm64。如果uname -m返回aarch64先只部署 FNS 与 CLIPerlite 需要自行构建或等待、选择经过验证的 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.ymlfns-cli-config.yaml.example稍后复制为fns-cli/config/config.yamlperlite.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--tail200fast-note-sync-service浏览器打开http://云服务器_IP:9000如果页面打不开先在 云服务器 局域网内排查不要第一时间做公网穿透curl-Ihttp://127.0.0.1:9000dockercompose logs--tail200fast-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--tail100fast-note-sync-service如果只在可信局域网使用也建议关闭如果准备接入公网更不能长期开放匿名注册。五、第二阶段配置 CLI 并首次只拉取5.1. 编写 CLI 配置创建/home/caimingyang/obsidian/fns-cli/config/config.yaml内容如下server: api:http://fast-note-sync-service:9000token:请替换为在 FNS Web 管理端生成的 JWT Tokenvault:知识库sync: watch_path:/app/vaultsync_notes:truesync_files:truesync_config:falseexclude_patterns: -.git/**-.trash/**-*.tmp-.fns_state.jsonfile_chunk_size:524288client: reconnect_max_retries:15reconnect_base_delay:3heartbeat_interval:30client_type:fns-clilogging: level:INFOfile:需要替换的只有两个核心值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执行pulldockercompose 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--tail200fns-clidockercompose logs--tail100perlitedockercompose logs--tail100perlite_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 和图片反向同步修改一篇专用测试笔记另一端收到修改不产生重复文件展示层打开8090Perlite 能显示目录、正文、图片和链接持久化重启容器或 NAS账户、Token、Vault、页面仍存在恢复层从备份恢复一份测试数据可以打开并核对不只是“有备份文件”7.2. 做一次端到端测试建议在 Obsidian 创建FNS-部署验收.md测试内容至少包含中文文件名。一张图片。一个[[双向链接]]。一个标签。一个代码块。然后按顺序检查电脑端保存测试笔记。手机端确认收到。云服务器fns-cli/vault确认文件和图片出现。Perlite 确认页面能够打开。在另一台 Obsidian 设备修改测试文字。检查其他端是否同步更新。7.3. 做一次重启测试dockercompose restartdockercomposepsdockercompose logs--tail100fast-note-sync-servicedockercompose logs--tail100fns-cli最后再重启一次 云服务器确认FNS 账户和 Vault 没有丢失。CLI 能根据.fns_state.json继续增量同步。Perlite 仍然读取同一个 Vault。容器没有因为目录权限变化而启动失败。八、同步、展示、备份要分开管理8.1. 三条边界同步、展示和备份的职责边界这张图里最重要的不是三个组件而是三句话同步会传播错误。只读挂载不等于访问控制。有备份文件不等于能够恢复。8.2. 至少备份哪些目录目录内容建议fast-note-sync/storage默认数据库、附件和服务端数据定期快照一致性备份时暂停写入或使用 云服务器 一致性快照fast-note-sync/config服务端配置每次改配置后备份fns-cli/configCLI 配置与 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 反向代理要支持 WebSocketPerlite 前面要有访问控制。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_PATHNotes、/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 ServiceFast Note Sync Obsidian 插件FastNodeSync-CLIFast Note Sync Service Docker HubPerlitePerlite Docker SetupPerlite Docker Hub Tags