用 Docker 把 Obsidian 搬到浏览器:部署、同步与 SSO 配置笔记

用 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 中维护多站点统一认证规则。

访问链路如下:

  1. 浏览器通过 HTTPS 访问 Obsidian 域名。
  2. 外层反向代理与 NPM 将请求转给 OAuth2 Proxy。
  3. OAuth2 Proxy 通过群晖 SSO Server 的 OIDC 完成登录。
  4. 登录后,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 页面登录。操作时注意:

  1. 确认 SSO 入口已保护成功。
  2. 移除两个变量后,在 Portainer 执行 Update the stack,应用新的容器配置;只重启旧容器不会改变环境变量。
  3. 确认 NPM Access List 没有额外启用 Basic Auth。
  4. 用无痕窗口重新测试。

记录边界:本次已确认 HTTP 上游可访问;移除密码后"未生效"的现象尚未完成定位,不能把"无弹框且仅经 SSO 访问"写成已验证结果。

6. 接入 Self-hosted LiveSync

把服务器端 Obsidian 当作一个新设备,而不是直接把 CouchDB 数据目录挂载为笔记库。

  1. 原设备完成同步并备份。
  2. 在服务器创建独立的本地 Vault,例如 /vaults/MyNotes。
  3. 安装 Self-hosted LiveSync。
  4. 从现有设备生成 Setup URI,在新设备导入。
  5. 首次选择从远端获取数据的方向,避免用空库覆盖远端。

Setup URI 可能包含连接凭据或加密信息,应像密码一样保管,不发布到笔记或博客中。多个 Vault 分别连接其对应数据库。

7. 安装插件后黑屏:本次有效的恢复方法

现象:浏览器还能显示 Selkies 面板,但 Obsidian 界面消失。

本次通过临时禁用第三方插件恢复了界面:

  1. 停止 Obsidian 容器。
  2. 在宿主机找到目标 Vault 的 .obsidian 目录。
  3. 将 community-plugins.json 重命名为 community-plugins.json.bak。
  4. 启动容器并刷新网页。
  5. 逐个启用插件,定位触发问题的插件。

不要直接删除 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 版本可能需要调整。

相关推荐
流星白龙2 小时前
【Docker】14.存储卷与管理卷Volume
运维·docker·容器
amcomputer2 小时前
Docker 常用命令总结:从安装到日常使用
运维·docker·容器
一级新生4 小时前
Dify 日常使用手册(小白版)
人工智能·docker·云原生·eureka·dify·ai工具
YuanDaima20485 小时前
系统架构与数据库理论基础:PostgreSQL 与 pgvector
数据库·人工智能·docker·postgresql·系统架构·产品经理
程序员老赵20 小时前
Docker 部署 Hue:轻松搭建数据库与数据仓库 SQL 查询平台
sql·docker·开源
m0_617493941 天前
昇腾 NPU Docker 部署实战:解决 stub library 报错并实现模型正常加载
运维·docker·容器
xixiaoyunya1 天前
Docker 常用命令与实战:从入门到部署一个简单应用
docker·容器·eureka
吴声子夜歌1 天前
Docker入门与实战——Docker Swarm模式(跨主机容器集群管理)
docker
pirateeee1 天前
在ubuntu中通过 docker 搭建 yocto 开发环境
ubuntu·docker·eureka