用 Docker 把 Obsidian 搬到浏览器:部署、同步与 SSO 配置笔记
我希望在不同电脑上打开网页,就能继续使用同一个 Obsidian,包括插件配置和授权信息。比较 Ignis 与 LinuxServer 两种方案后,最终选择在服务器上运行 LinuxServer Obsidian,通过 Selkies 在浏览器中操作。
本文记录部署结构、已验证的连接方式,以及遇到问题后的处理方法。所有域名、路径和凭据均为示例或占位符,不包含真实账号、密钥、内网地址或笔记内容。
1. 为什么选择 LinuxServer
Ignis 通过兼容层让 Obsidian 前端运行在浏览器,笔记文件保存在服务器,交互更接近普通网页。但浏览器本地存储中的插件状态可能随电脑、浏览器而变化;插件兼容性也需要逐个验证。
LinuxServer 运行完整的 Linux 桌面版 Obsidian,再将画面传输到浏览器。不同电脑连接的是同一个服务器端实例,因此服务器中已经保存的插件配置和登录状态通常可以继续使用。
两种方案的差异:
| 项目 | Ignis | LinuxServer Obsidian |
|---|---|---|
| 界面运行位置 | 浏览器 | 服务器 |
| 浏览器接收内容 | 应用代码、笔记数据及缓存 | 远程应用画面与交互数据 |
| 插件兼容性 | 受兼容层及浏览器限制 | 更接近 Linux 桌面版 |
| 换电脑后的状态 | 取决于插件保存位置 | 通常沿用同一个服务器端环境 |
| 使用体验 | 更像 Web 应用 | 更像远程桌面 |
LinuxServer 并不能保证所有插件永不重新授权:令牌到期、仅保存于内存的密钥、系统凭据加密机制,都可能影响重启后的状态。
2. 部署结构:先直接访问,再接入 SSO
最终选择一套 OAuth2 Proxy 对应一个 Obsidian,避免在 NPM 中维护多站点统一认证规则。
访问链路如下:
- 浏览器通过 HTTPS 访问 Obsidian 域名。
- 外层反向代理与 NPM 将请求转给 OAuth2 Proxy。
- OAuth2 Proxy 通过群晖 SSO Server 的 OIDC 完成登录。
- 登录后,OAuth2 Proxy 转发到 Docker 内网中的 Obsidian HTTP 入口。
本次验证通过的上游地址是 http://obsidian:3000/。 HTTPS 3001 的连接在本次环境中未成功,具体原因未继续定位。不能据此认为镜像的 HTTPS 入口普遍不可用。
3. 完整 Stack 示例
下面是用于复用的模板,不是原环境配置的完整导出。部署前需要填写环境变量,并确认目录权限。
bash
services:
obsidian:
image: lscr.io/linuxserver/obsidian:latest
container_name: obsidian
restart: unless-stopped
environment:
PUID: "${APP_UID}"
PGID: "${APP_GID}"
TZ: Asia/Shanghai
LC_ALL: zh_CN.UTF-8
volumes:
- /volume1/docker/obsidian/config:/config
- /volume1/docker/obsidian/vaults:/vaults
shm_size: "1gb"
# 接入认证后不发布宿主机端口,避免绕过 OAuth2 Proxy。
obsidian-auth:
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.0
container_name: obsidian-auth
restart: unless-stopped
depends_on:
- obsidian
ports:
- "4180:4180"
environment:
OAUTH2_PROXY_HTTP_ADDRESS: "0.0.0.0:4180"
OAUTH2_PROXY_PROVIDER: "oidc"
OAUTH2_PROXY_PROVIDER_DISPLAY_NAME: "SSO 登录"
OAUTH2_PROXY_OIDC_ISSUER_URL: "${SSO_ISSUER}"
OAUTH2_PROXY_CLIENT_ID: "${SSO_CLIENT_ID}"
OAUTH2_PROXY_CLIENT_SECRET: "${SSO_CLIENT_SECRET}"
OAUTH2_PROXY_REDIRECT_URL: "${SSO_CALLBACK_URL}"
OAUTH2_PROXY_SCOPE: "openid email profile"
OAUTH2_PROXY_EMAIL_DOMAINS: "*"
OAUTH2_PROXY_UPSTREAMS: "http://obsidian:3000/"
OAUTH2_PROXY_PROXY_WEBSOCKETS: "true"
OAUTH2_PROXY_REVERSE_PROXY: "true"
OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"
OAUTH2_PROXY_COOKIE_SECRET: "${OAUTH_COOKIE_SECRET}"
OAUTH2_PROXY_COOKIE_NAME: "__Host-obsidian_session"
OAUTH2_PROXY_COOKIE_PATH: "/"
OAUTH2_PROXY_COOKIE_SECURE: "true"
OAUTH2_PROXY_COOKIE_HTTPONLY: "true"
OAUTH2_PROXY_COOKIE_SAMESITE: "lax"
OAUTH2_PROXY_COOKIE_EXPIRE: "168h"
部署时的环境变量示例:
bash
APP_UID=1000
APP_GID=1000
SSO_ISSUER=https://sso.example.com/REPLACE_WITH_ACTUAL_ISSUER
SSO_CLIENT_ID=REPLACE_WITH_CLIENT_ID
SSO_CLIENT_SECRET=REPLACE_WITH_CLIENT_SECRET
SSO_CALLBACK_URL=https://notes.example.com:7777/oauth2/callback
OAUTH_COOKIE_SECRET=REPLACE_WITH_GENERATED_SECRET
说明:
- UID、GID 使用有权读写挂载目录的宿主机用户值,可通过
id 用户名查询。 SSO_ISSUER必须复制群晖 Well-known 文档中的issuer,不是 Well-known URL 本身。SSO_CALLBACK_URL必须与群晖登记的回调地址一致,包括实际外部端口;标准 443 不必显式填写。EMAIL_DOMAINS: "*"并不是用户白名单。应在群晖 SSO 应用中限制允许登录的用户。openid email profile是否可用取决于身份服务版本及配置;如果报 scope 或邮箱声明错误,需要依据实际返回信息调整。4180只供可信反向代理访问,不做公网端口转发。需要进一步隔离时可改用 NPM 与认证服务之间的专用 Docker 网络。- 这里保留本次讨论的镜像版本。升级前查看发行说明;Obsidian 的
latest是浮动标签,稳定后可记录或固定实际使用版本。
生成 Cookie 密钥:
bash
openssl rand -base64 32 | tr '+/' '-_'
密钥只填入私有环境变量配置,不写进公开博客、截图或仓库。已有部署不要替换成新目录,否则会看起来像配置丢失。
4. 群晖 SSO 与 NPM
群晖 SSO Server
启用 OIDC,为这套 Obsidian 注册一个应用,登记回调地址:
bash
https://notes.example.com:7777/oauth2/callback
保存 Client ID 与 Client Secret,限制应用可访问用户。群晖的 SSO 地址需要同时被浏览器和 OAuth2 Proxy 访问,且证书可信。不要把 QuickConnect 地址直接当作 OIDC 服务地址。
Nginx Proxy Manager
| 配置项 | 设置 |
|---|---|
| Domain Names | notes.example.com |
| Scheme | http |
| Forward Host / IP | OAuth2 Proxy 所在主机的可达地址 |
| Forward Port | 4180 |
| Websockets Support | 开启 |
| Access List | Publicly Accessible,由 OAuth2 Proxy 认证 |
| 浏览器入口 | HTTPS |
NPM 的后端必须指向 OAuth2 Proxy,而不是直接指向 Obsidian。多层反代还应保留正确的外部 Host、端口与 X-Forwarded-Proto,避免登录回调丢端口。
此方案不需要独立的认证子域名,也不需要 NPM auth_request 配置。
5. 原来的弹框登录如何处理
LinuxServer 的 CUSTOM_USER、PASSWORD 启用 HTTP Basic Auth,因此浏览器会弹出原生密码框,而不是普通网页登录页。
接入 OAuth2 Proxy 后,计划移除这两个变量,让用户只经过 SSO 页面登录。操作时注意:
- 确认 SSO 入口已保护成功。
- 移除两个变量后,在 Portainer 执行 Update the stack,应用新的容器配置;只重启旧容器不会改变环境变量。
- 确认 NPM Access List 没有额外启用 Basic Auth。
- 用无痕窗口重新测试。
记录边界:本次已确认 HTTP 上游可访问;移除密码后"未生效"的现象尚未完成定位,不能把"无弹框且仅经 SSO 访问"写成已验证结果。
6. 接入 Self-hosted LiveSync
把服务器端 Obsidian 当作一个新设备,而不是直接把 CouchDB 数据目录挂载为笔记库。
- 原设备完成同步并备份。
- 在服务器创建独立的本地 Vault,例如
/vaults/MyNotes。 - 安装 Self-hosted LiveSync。
- 从现有设备生成 Setup URI,在新设备导入。
- 首次选择从远端获取数据的方向,避免用空库覆盖远端。
Setup URI 可能包含连接凭据或加密信息,应像密码一样保管,不发布到笔记或博客中。多个 Vault 分别连接其对应数据库。
7. 安装插件后黑屏:本次有效的恢复方法
现象:浏览器还能显示 Selkies 面板,但 Obsidian 界面消失。
本次通过临时禁用第三方插件恢复了界面:
- 停止 Obsidian 容器。
- 在宿主机找到目标 Vault 的
.obsidian目录。 - 将
community-plugins.json重命名为community-plugins.json.bak。 - 启动容器并刷新网页。
- 逐个启用插件,定位触发问题的插件。
不要直接删除 plugins 目录,里面可能保存插件及授权配置。本次尚未确认具体是哪个插件或什么机制导致黑屏。
8. 显示效果:优先优化文字
Selkies 是远程画面传输。笔记场景应优先保证文字清晰,而不是提高帧率。
以下是基于面板给出的调优起点,效果需要实际比较,并非全部已经验证:
| 设置 | 建议起点 |
|---|---|
| 编码器 | H.264 |
| 码率控制 | CRF |
| 帧率 | 30 FPS |
| 视频 CRF | 20,必要时降到 18 |
| 静态区域优化 | 开启 |
| 静态区域优化 CRF | 14,可在 12~15 间比较 |
| 静态优化连发帧数 | 先保持 5 |
| 全彩 4:4:4 | 开启 |
| HiDPI(像素完美) | 开启 |
| Turbo | 先关闭 |
| 浏览器缩放 | 先保持 100% |
CRF 越低通常画质越好,但带宽消耗增加。界面太小时优先调整 Selkies 的界面缩放或 Obsidian 字号,避免多层缩放叠加。
9. 常见连接问题
There was a problem connecting to the upstream server
先查看 OAuth2 Proxy 日志:
bash
docker logs --tail 80 obsidian-auth
| 线索 | 排查方向 |
|---|---|
no such host |
服务名错误或不在同一 Docker 网络 |
connection refused |
端口错误、服务尚未就绪 |
i/o timeout |
网络或后端响应异常 |
| TLS / certificate | HTTPS 协议或证书问题 |
本次最终使用 http://obsidian:3000/ 解决上游连接问题。这里的 obsidian 是 Compose 服务名,3000 是容器内部端口,不是宿主机映射端口。
没感觉到登录过程
已有 OAuth2 Proxy Cookie,或浏览器已有群晖 SSO 会话,可能让跳转很快完成。用新的无痕会话测试,并核对 NPM 是否确实指向 4180。直接打开后端映射端口不属于认证链路。
10. 多库与代理:目前保留的选择
多个 Vault
同一 LinuxServer 容器中的多个网页连接同一个远程环境,不能仅添加 ?vault=... 就得到相互独立的桌面。若希望不同网址稳定对应不同库,采用一库一个容器,分别使用独立 /config。
为了降低配置复杂度,当前选择每个 Obsidian 配一个 OAuth2 Proxy。群晖 SSO Server 可以共用,但各应用的回调和访问权限分别管理。
插件下载代理
讨论过通过 Electron 的 --proxy-server 或 PAC 配置代理。仅设置 HTTP_PROXY、HTTPS_PROXY 不能保证所有 Obsidian 请求生效。
本次尚未确认镜像启动参数的持久化注入方式,也未验证插件更新代理成功,因此这里不提供未经验证的 Stack 参数。 后续应先查看当前镜像的启动脚本,再添加配置。
11. 备份与后续验证
需要备份:
/config对应的宿主机目录:程序用户配置及部分状态。/vaults对应的宿主机目录:Markdown、附件、库内插件配置。- 私有保存的 Stack 环境变量与 Cookie Secret。
LiveSync 是同步机制,不替代历史备份。下一步重点验证:无痕登录、旧 Basic Auth 是否已移除、容器重建后的插件状态,以及常用插件逐个启用后的稳定性。
参考资料
本文是个人部署记录。已验证结果与建议配置已分别标明;不同镜像及 SSO 版本可能需要调整。