SilverBullet Docker Compose 部署实战

SilverBullet v2.12.1 官方镜像 Docker Compose 从零部署实测:镜像选型与双通道获取、SB_FOLDER/SB_USER 单空间初始化、space 目录 bind mount、认证与页面读写验证、升级回滚与 HTTPS 前置要求(Ubuntu 24.04 + Docker 29 实测回显)。

SilverBulletSilverBullet知识库Docker2026/10/28

SilverBullet Docker Compose 部署实战

目标:在一台 Ubuntu 服务器上用官方镜像与 Docker Compose 从零部署一套 SilverBullet v2.12.1 笔记服务:镜像选型与双通道获取、单空间模式初始化(登录账号 + API 令牌)、space 目录持久化、认证登录与页面读写实测、升级与回滚、HTTPS 前置要求,全部命令与回显均在下述环境实测通过。

一、环境信息

本文所有命令与回显均在以下环境实测:

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64),文件系统大小写敏感(SilverBullet 服务端要求,见「常见问题」)
  • Docker Engine:29.9.2(含 Docker Compose v2 插件)
  • SilverBullet:v2.12.1(官方镜像 ghcr.io/silverbulletmd/silverbullet:2.12.1,构建标识 2.12.1-0-g4d5eac53-2026-10-12T06-21-29Z,amd64/linux,镜像体积约 1.08 GB)
  • 部署目录:/opt/silverbullet/(编排文件目录),数据目录 /opt/silverbullet/data(bind mount 到服务内 /data)
  • 服务端口:宿主 8147 → 服务内 3000
  • 演示账号:admin / q1w2e3r4;演示 API 令牌:sbtoken-q1w2e3r4-demo
  • 访问入口:http://<服务器IP>:8147/(本文以 127.0.0.1:8147 为例)

⚠️ 网络说明:官方主发布通道为 GitHub 容器仓库(GHCR),本次实测 GHCR 的层下载长期停滞(manifest 元数据可秒级获取,层内容无进展);改用官方同名 Docker Hub 仓库 zefhemel/silverbullet(官方文档明确两者为同一镜像)经已配置的镜像加速器拉取成功,层摘要与 GHCR 清单一致。当前实测可用渠道:Docker Hub zefhemel/silverbullet:2.12.1;GHCR 通道在部分网络环境可用,读者可先试 GHCR、失败即换 Docker Hub,命令见「二、镜像选型说明」。

二、镜像选型说明

SilverBullet 官方以容器镜像为主要发布形态,官方文档(Install/Docker)给出的发布事实如下,选型前先弄清标签体系:

镜像标签含义适用
:latest始终指向最新稳定版(release)想跟随官方稳定版自动升级
:edge始终指向 main 分支最新提交构建(旧标签 :v2 同镜像)追新测试,不建议生产
:2.12.1 这类版本号锁定具体版本生产固定版本(本文采用)
上述任一标签加 -slim 后缀(如 :2.12.1-slim、:latest-slim)不含 Chromium 的精简变体不需要运行时 API 的极小资源场景

关键差异解读:默认镜像内置 Chromium(本镜像内路径 /usr/bin/chromium-headless-shell,由环境变量 CHROMIUM_PATH 预设),用于服务端运行时 API(无头浏览器里执行 Space Lua);-slim 变体体积小但没有 Chromium,依赖运行时 API 的 sb eval/sb query/截图等能力不可用。本文选 :2.12.1 锁版本 + 默认(非 slim)变体:生产要版本可回滚,且完整功能可用。

镜像支持的架构:64 位 Intel/AMD(amd64)、64 位 ARM(arm64,树莓派 64 位系统/Apple Silicon)、32 位 ARM(armv7)。

两个官方仓库为同一镜像,可任选:

# 官方主通道:GHCR(GitHub 容器仓库)
docker pull ghcr.io/silverbulletmd/silverbullet:2.12.1

# 备用通道:Docker Hub 官方同名仓库(同一镜像、同一层摘要)
docker pull zefhemel/silverbullet:2.12.1

本次实测回显(Docker Hub 通道成功拉取,输出节选):

cb02a4579ce6: Pull complete
934cf8a51fb7: Download complete
934cf8a51fb7: Pull complete
Digest: sha256:370a3bf2daa37b7dbba62183085c57d5d43862189ea127e0765646d230561d9d
Status: Downloaded newer image for zefhemel/silverbullet:2.12.1
docker.io/zefhemel/silverbullet:2.12.1

若两个通道只能到达其一,把编排文件里 image: 字段换成实际拉到的那个引用即可,服务行为完全一致。本机为便于按官方主通道引用,将拉取的镜像补打一个本地标签(同一镜像,不产生额外占用):

docker tag zefhemel/silverbullet:2.12.1 ghcr.io/silverbulletmd/silverbullet:2.12.1

验证镜像在册与体积:

docker images --format '{{.Repository}}:{{.Tag}}  {{.Size}}' | grep silverbullet

实测回显:

zefhemel/silverbullet:2.12.1  1.08GB
ghcr.io/silverbulletmd/silverbullet:2.12.1  1.08GB

三、前置条件

  1. 服务器已安装 Docker Engine 与 Docker Compose v2 插件,验证:
docker version --format 'client {{.Client.Version}} / server {{.Server.Version}}'
docker compose version

实测回显:

client 29.9.2 / server 29.9.2
Docker Compose version v2.40.0
  1. 端口未被占用(本文用宿主 8147,换端口同理):
ss -ltn | grep :8147 || echo '8147 free'

实测回显:

8147 free
  1. 创建部署目录(编排文件与数据目录放在一起,便于备份整体迁移):
mkdir -p /opt/silverbullet/data
cd /opt/silverbullet

data 目录会通过 bind mount 映射为服务内 /data,即 SilverBullet 的数据根目录:页面文件、认证数据都落在这里。目录留空即可,服务首启会自动生成欢迎首页。

⚠️ 注意:数据目录所在文件系统必须大小写敏感(Linux 原生 ext4/xfs 均满足)。官方明确不推荐把数据目录放在 macOS(APFS 默认大小写不敏感)或 Windows(NTFS)上,也不要放在 NFS/SMB 网络挂载上——mtime 语义不可靠会破坏同步引擎,网络挂载场景需改用轮询监听(SB_FS_WATCH=poll,见配置篇)。

四、部署步骤

4.1 编写 compose.yml

在 /opt/silverbullet/ 下创建 compose.yml(文件名用 compose.yml 或 docker-compose.yml 均可,Compose v2 优先识别前者):

name: silverbullet-2.12.1-stack

services:
  silverbullet:
    image: ghcr.io/silverbulletmd/silverbullet:2.12.1
    container_name: silverbullet-2.12.1-web
    restart: unless-stopped
    environment:
      # 单空间模式(经典形态):space 目录 + 登录账号 + API 令牌
      SB_FOLDER: /data
      SB_USER: admin:q1w2e3r4
      SB_AUTH_TOKEN: "sbtoken-q1w2e3r4-demo"
      SB_HOSTNAME: 0.0.0.0
    volumes:
      - /opt/silverbullet/data:/data
    ports:
      - "8147:3000"

逐项说明:

  • name: silverbullet-2.12.1-stack:显式 Compose 项目名,多套服务并存时 docker compose down 只会操作本项目,避免误删其他栈;
  • image:锁定 2.12.1 版本(升级见「十、常用运维命令」);
  • container_name:固定服务实例名,便于 docker logs/监控取用;
  • SB_FOLDER: /data:指定要对外提供的数据目录为 /data(即 bind mount 进来的宿主目录)。设置了 SB_FOLDER 后服务以单空间模式启动,/data 整个目录就是一个 space;
  • SB_USER: admin:q1w2e3r4:开启登录认证,格式 用户名:密码。Web 与 API 均需登录;
  • SB_AUTH_TOKEN:HTTP API 的 Bearer 令牌(Authorization: Bearer <token>),供脚本/curl 免会话调用;
  • SB_HOSTNAME: 0.0.0.0:监听所有网卡(官方镜像已内置该默认值,写出来是明示);
  • volumes:宿主数据目录 ↔ 服务内 /data,页面即文件,宿主侧可直接备份;
  • ports: "8147:3000":宿主 8147 → 服务内 3000(容器镜像固定监听 3000,由内置环境变量 SB_PORT=3000 决定)。

4.2 启动并观察首启日志

cd /opt/silverbullet
docker compose up -d

实测回显:

 Network silverbullet-2121-stack_default Creating
 Network silverbullet-2121-stack_default Created
 Container silverbullet-2.12.1-web Creating
 Container silverbullet-2.12.1-web Created
 Container silverbullet-2.12.1-web Starting
 Container silverbullet-2.12.1-web Started

查看启动日志确认引导形态与监听地址:

docker compose logs --no-log-prefix | head -6

实测回显:

Will run SilverBullet with UID 0, inferred from the owner of /data (set PUID environment variable to override)
2026-10-28T06:44:46.176012Z  INFO silverbullet::server: SilverBullet 2.12.1-0-g4d5eac53-2026-10-12T06-21-29Z
2026-10-28T06:44:46.176052Z  INFO silverbullet::server: Data folder: /data
2026-10-28T06:44:46.176066Z  INFO silverbullet::boot: boot mode: single-space (SB_USER is set)
2026-10-28T06:44:46.178896Z  INFO silverbullet_server_runtime_chrome::config: runtime Chrome detected: /usr/bin/chromium-headless-shell
2026-10-28T06:44:46.180393Z  INFO silverbullet::server: SilverBullet is now running: http://0.0.0.0:3000

关键行解读:

  • boot mode: single-space (SB_USER is set):因设置了 SB_USER,服务进入单空间模式(经典形态)。若空目录且无任何 SB_* 变量启动,服务会进入 unconfigured 引导模式,把首页 307 重定向到 /.setup/ 安装向导(多空间 Dashboard 形态)——两种引导的差异见「十二、常见问题」第 1 条;
  • runtime Chrome detected:默认镜像内置的 Chromium 被识别,运行时 API 可用;
  • Will run SilverBullet with UID 0, inferred from the owner of /data:入口脚本按 /data 目录属主的 UID/GID 运行服务进程,使文件权限天然匹配(本文实验目录属主为 root,故 UID 0;生产建议用普通属主目录或显式 PUID/PGID,见「五、数据持久化说明」)。

4.3 状态与健康检查

docker compose ps

实测回显:

NAME                      IMAGE                                        COMMAND                  SERVICE        CREATED        STATUS                                     PORTS
silverbullet-2.12.1-web   ghcr.io/silverbulletmd/silverbullet:2.12.1   "/sbin/tini -- /dock…"   silverbullet   1 second ago   Up Less than a second (health: starting)   0.0.0.0:8147->3000/tcp, [::]:8147->3000/tcp

镜像内置健康检查(对服务内 /.instance 端点做 curl --fail),启动数秒后 health: starting 变为 healthy。再用官方健康检查端点确认服务可用:

curl -s http://127.0.0.1:8147/.ping -w "\nHTTP %{http_code}\n"

实测回显:

OK
HTTP 200

浏览器打开 http://<服务器IP>:8147/,输入 admin / q1w2e3r4 登录即可进入编辑器。

4.4 认证行为实测(API 视角)

未携带凭据访问文件 API 会被拒绝(401):

curl -s http://127.0.0.1:8147/.fs/index.md -w "\nHTTP %{http_code}\n"

实测回显:

Unauthorized
HTTP 401

错误口令登录返回错误 JSON(注意 HTTP 状态码仍为 200,错误信息在响应体里):

curl -s -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'username=admin&password=wrong-pass' \
  http://127.0.0.1:8147/.auth -w "\nHTTP %{http_code}\n"

实测回显:

{"status":"error","error":"Invalid username and/or password"}
HTTP 200

正确口令登录,服务端下发会话 Cookie(-c 保存):

curl -s -c /opt/silverbullet/cookies.txt -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'username=admin&password=q1w2e3r4' \
  http://127.0.0.1:8147/.auth -w "\nHTTP %{http_code}\n"

实测回显:

{"status":"ok","redirect":"/"}
HTTP 200

带 Cookie 读取首页内容(输出节选——实测首尾,中间为服务自动生成的欢迎页正文):

curl -s -b /opt/silverbullet/cookies.txt http://127.0.0.1:8147/.fs/index.md -w "\nHTTP %{http_code}\n"

实测回显(节选):

Hello 👋!

Welcome to the wondrous world of SilverBullet. A world that once you discover and appreciate, you'll never want to leave.
……(中间略:手册链接、快速笔记/日志/任务等查询块占位)
HTTP 200

💡 说明:全新空数据目录首启时,服务端会自动生成这份欢迎首页 index.md(含若干查询块示例),落盘于 /opt/silverbullet/data/index.md,可直接编辑或整页替换。查询块(${query[[…]]})由浏览器端索引渲染,curl 看到的是原始 Markdown 文本。

再用 Bearer 令牌写一个新页面(免会话,脚本自动化常用):

printf '# 部署手记\n\n本页由 HTTP API 写入,验证页面即文件。\n' | \
curl -s -X PUT -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' --data-binary @- \
  http://127.0.0.1:8147/.fs/Manual/Deploy-Notes.md -w "\nHTTP %{http_code}\n"

实测回显:

OK
HTTP 200

4.5 查看客户端配置确认服务形态

curl -s -b /opt/silverbullet/cookies.txt http://127.0.0.1:8147/.config

实测回显(单行 JSON,格式化展示):

{
  "spaceFolderPath": "/data",
  "spaceName": "SilverBullet",
  "indexPage": "index",
  "readOnly": false,
  "logPush": false,
  "enableClientEncryption": true,
  "accountManaged": false,
  "shellBackend": "local",
  "disableServiceWorker": false,
  "syncProtocolVersion": 2,
  "revisions": "unmanaged",
  "spacePrefixes": [],
  "shareOwnerId": "2d21cb0e…(64 位十六进制摘要)"
}

关键字段:spaceFolderPath 即服务内 space 路径;indexPage 首页名;accountManaged: false 表明当前为单空间环境变量认证(非多空间账号托管);enableClientEncryption: true 表示认证开启时客户端加密可选。

五、数据持久化说明

SilverBullet 是「文件夹即数据库」的设计:/opt/silverbullet/data 里就是一个 space 的全部内容——每个页面一个 .md 文件、子页面即子目录、外加一个认证数据文件。上一步 API 写入的页面,在宿主侧直接可见:

find /opt/silverbullet/data -type f | sort

实测回显:

/opt/silverbullet/data/.silverbullet.auth.json
/opt/silverbullet/data/Manual/Deploy-Notes.md
/opt/silverbullet/data/index.md
  • index.md:首页(服务首启自动生成);
  • Manual/Deploy-Notes.md:上一步 API 写入的页面——页面即文件,任何编辑器/git/rsync 都能直接处理;
  • .silverbullet.auth.json:认证凭据文件(账号口令与令牌的哈希),随数据目录一起备份;轮换 SB_USER/SB_AUTH_TOKEN 环境变量重启后该文件同步更新。

重启持久化验证——重启服务后页面原样可读:

cd /opt/silverbullet
docker compose restart
sleep 3
curl -s -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' \
  http://127.0.0.1:8147/.fs/Manual/Deploy-Notes.md -w "\nHTTP %{http_code}\n"

实测回显:

 Container silverbullet-2.12.1-web Restarting
 Container silverbullet-2.12.1-web Started
# 部署手记

本页由 HTTP API 写入,验证页面即文件。

HTTP 200

文件属主与 UID/GID:官方入口脚本会探测 /data 映射目录的属主(UID/GID)并以同一身份运行服务进程,宿主侧文件权限「天然匹配」,无需手工 chown。如需固定身份(例如多服务共用账号),显式设置 PUID/PGID 环境变量覆盖:

    environment:
      PUID: 1000
      PGID: 1000

💡 技术拓展:容器化应用持久化有两条常见路线——bind mount(宿主目录直挂,本文采用)与命名卷(Docker 卷区托管)。bind mount 的优势是数据形态透明(页面就是目录里的 .md 文件,备份=复制目录、迁移=rsync 目录),与 SilverBullet「文件所有权」的产品理念一致;命名卷的优势是初始化与权限由 Docker 托管、便于卷级快照,但查看内容要进卷区找路径。SilverBullet 场景推荐 bind mount。

六、环境变量表

本篇编排实际使用的环境变量与镜像内置默认值(经镜像元数据实查):

变量本文取值说明
SB_FOLDER/data要对外提供的数据目录;设了它即单空间模式,/data 整体为一个 space
SB_USERadmin:q1w2e3r4单空间登录账号,格式 用户名:密码;设置即开启认证
SB_AUTH_TOKENsbtoken-q1w2e3r4-demoHTTP API 的 Bearer 令牌(演示值;生产请换强随机串)
SB_HOSTNAME0.0.0.0(镜像内置默认)监听地址;默认即全网卡,编排里写出是明示
SB_PORT3000(镜像内置默认)服务内监听端口;容器场景无需改动,对外换端口改 ports 即可
CHROMIUM_PATH/usr/bin/chromium-headless-shell(镜像内置)运行时 API 使用的无头浏览器路径
PUID / PGID未设置(默认推断 /data 属主)需固定运行身份时显式设置

完整的环境变量体系(网络、认证锁定、搜索、文件监听、多空间 Dashboard 等)与 CONFIG 配置页说明,见本专栏后续的配置参数篇;本篇只列部署直接相关项。

⚠️ 注意:SB_USER/SB_AUTH_TOKEN 属单空间模式的认证方式。SilverBullet v2 的默认形态是多空间模式(空目录首启进入 /.setup/ 向导,账号/空间/访问级别由 Dashboard 管理)。单空间模式官方标记为 legacy 但仍完整支持,适合「单用户、脚本自动化、目录即备份」的自托管场景;两种模式对比与迁移见「十二、常见问题」第 1 条。

七、配置参数说明

对 4.1 编排中每个参数的选择依据做逐项复盘:

  • 版本锁定 2.12.1 而非 :latest:生产环境升级应当是显式动作(改版本号 → pull → 重建),:latest 会在某次 docker compose up -d 时静默引入新版本。官方升级流程两者都支持,见「十、常用运维命令」;
  • 显式 container_name 与项目 name:单机上会共存多套 Compose 项目,显式命名让 docker compose down/日志检索不会误伤其他项目;
  • restart: unless-stopped:服务器重启或服务崩溃后自动拉起(手工 stop 的除外),自托管服务的基本盘;
  • 认证双通道:Web 交互用 SB_USER 账号,脚本自动化用 SB_AUTH_TOKEN 令牌,各走各的,避免脚本里保存会话 Cookie 的过期烦恼;
  • 数据目录整体 bind mount:不拆分「页面目录」「认证文件」多个挂载点,整体一个 /data 与官方编排一致,备份与迁移心智最简单。

八、配置文件挂载

SilverBullet 的服务端几乎没有「配置文件」要挂载——配置分三处:

  1. 页面即配置:空间级配置(首页名、视图、标签 schema、Lua 扩展)放在 space 内的 CONFIG.md 页面里,随数据目录落盘,天然持久化;
  2. 环境变量:服务级参数(监听、认证、路径)走环境变量,即 4.1 编排的 environment: 段;
  3. 启动脚本钩子:官方镜像支持在 space 里建一个 CONTAINER_BOOT.md 页面,内容会在服务启动时作为 shell 脚本执行——典型用途是往基于 Alpine 的镜像里补装软件包(页面里写 apk add pandoc 一类命令,去掉注释即可生效)。该页面同样随数据目录持久化,重建实例后自动重放。

因此本篇无需额外挂载配置文件:/opt/silverbullet/data:/data 一个挂载点覆盖全部持久化状态。

九、等价 docker run

不用 Compose 的等价启动命令(与 4.1 编排完全同参):

docker run -d --restart unless-stopped \
  --name silverbullet-2.12.1-web \
  -p 8147:3000 \
  -v /opt/silverbullet/data:/data \
  -e SB_FOLDER=/data \
  -e SB_USER=admin:q1w2e3r4 \
  -e SB_AUTH_TOKEN=sbtoken-q1w2e3r4-demo \
  -e SB_HOSTNAME=0.0.0.0 \
  ghcr.io/silverbulletmd/silverbullet:2.12.1

验证:

curl -s http://127.0.0.1:8147/.ping -w "\nHTTP %{http_code}\n"

实测回显(与 Compose 形态一致):

OK
HTTP 200

长期维护仍建议用 Compose(声明式、可复查、升级流程标准化);docker run 适合快速验证。

十、常用运维命令

以下命令均在 /opt/silverbullet/ 编排目录内执行。

日常状态:

docker compose ps                     # 实例状态与健康检查
docker compose logs -f                # 跟踪日志
docker compose logs --no-log-prefix | head -6   # 首启引导形态回看

重启与停止:

docker compose restart                # 重启(数据不受影响,已实测)
docker compose stop                   # 停止(保留实例与数据)
docker compose up -d                  # 再次启动

stop + up -d 实测回显:

 Container silverbullet-2.12.1-web Stopping
 Container silverbullet-2.12.1-web Stopped
 Container silverbullet-2.12.1-web Starting
 Container silverbullet-2.12.1-web Started

升级流程(官方推荐:pull → stop → up,改了 image: 版本号后执行):

docker compose pull
docker compose stop
docker compose up -d

pull 实测回显(版本未变时提示在册复用):

 Image ghcr.io/silverbulletmd/silverbullet:2.12.1 Pulling
 Image ghcr.io/silverbulletmd/silverbullet:2.12.1 Pulled

⚠️ 注意:升级后浏览器客户端可能仍是旧版缓存,连续刷新页面两次(官方文档明示)才会完全激活新版;升级属数据目录不变的原地替换,回滚只需把 image: 版本号改回旧版重新 pull/up。

卸载清理(确认数据已备份后):

docker compose down                   # 停止并删除实例与本项目网络,数据目录保留
rm -rf /opt/silverbullet/data         # 连数据一起清除(危险动作,先备份!)

备份:整目录复制即可(页面即文件):

tar -czf /opt/backup/silverbullet-data-$(date +%F).tar.gz -C /opt/silverbullet data

恢复 = 解包回 /opt/silverbullet/data 后 docker compose up -d。空间级备份与页面级恢复的完整操作(含 API 导出单页)见本专栏后续的命令运维篇。

十一、HTTPS 前置说明

SilverBullet 纯 HTTP(局域网 IP 访问)即可编辑、索引、查询、本地密码登录——本文实测即 HTTP 形态。但官方文档(TLS 页)明确:HTTPS 才能解锁完整特性,这是浏览器对「安全上下文」的强制约束(Service Worker、加密存储、剪贴板 API 仅在 HTTPS/localhost 可用)。计划对外提供或手机 PWA 离线使用时,需要在服务前面加一层 TLS,先知道差距再选型:

能力纯 HTTP(局域网)HTTPS
编辑/索引/查询/Space Lua✅ 可用✅ 可用
离线启动、离线同步(PWA)❌ 仅在线模式✅ 完整支持
客户端本地数据加密❌✅
OIDC/SSO 登录❌✅
传输安全明文(凭据可被截获)加密

部署 HTTPS 的官方推荐路径(按场景):

  • 已有反代(Caddy/Nginx/Authelia/Authentik 等):直接反代到 127.0.0.1:8147 即可;

  • 云服务器 + Caddy:Caddy 自动申请续期 Let's Encrypt 证书,最小配置两行:

    notes.example.com {
        reverse_proxy localhost:8147
    }
    
  • Tailscale 内网:用 Tailscale 签发的 *.ts.net 证书,兼顾外网可达与 VPN 内网两种暴露面。

反代两条硬性注意(官方排障文档实锤,漏配现象见「十二、常见问题」第 5 条):

  1. 开启 HTTP/2:HTTP/1.1 下浏览器对同源仅 6 个并发连接,SilverBullet 的同步引擎会占满连接导致「Could not reach remote server」假死。Caddy 默认已启用;nginx 需 listen 443 ssl http2;(或 1.25.1+ 写 http2 on;)。验证命令:curl -s -o /dev/null -w '%{http_version}\n' https://你的域名/.ping 应输出 2;
  2. /.events 事件流不缓冲、不压缩:SilverBullet 靠 /.events 的 SSE 流推送实时更新(服务端每 30 秒心跳一次),反代若缓冲该路径,客户端会退化为轮询。nginx 需为 /.events 单独加 proxy_buffering off; gzip off; 的 location 块(官方文档给出完整片段,可直接照抄)。

十二、常见问题

Q1:空目录启动后浏览器被 307 到 /.setup/,和我预期的登录页不一样? 这是 v2 的两种引导形态:空数据目录且未设置任何 SB_* 环境变量时,服务以 unconfigured 模式启动(日志:boot mode: unconfigured (empty folder, no flags, no legacy env)),GET / 返回 307 重定向到 /.setup/ 安装向导(页面标题 SilverBullet Setup),走多空间 Dashboard 初始化(建管理员账号、建首个空间)。本文编排设置了 SB_FOLDER + SB_USER,日志明确显示 boot mode: single-space (SB_USER is set),跳过向导直接以单空间模式服务。两种都是官方形态:单空间=一个目录一个空间、环境变量认证(legacy 但完整支持);多空间=账号托管、空间访问级别(none/read/write)、OIDC 单点登录。想用多空间形态,去掉 SB_FOLDER/SB_USER/SB_AUTH_TOKEN 用空目录重启,进向导初始化即可。

Q2:登录接口返回 200 却没登录上? 看响应体而不是状态码:/.auth 的失败响应是 HTTP 200 + {"status":"error","error":"Invalid username and/or password"}(本文 4.4 实测),成功才是 {"status":"ok","redirect":"/"}。用 curl 判断登录结果要 grep 响应体里的 "status":"ok"。另外注意防暴力破解内置机制:默认连续失败 10 次(SB_LOCKOUT_LIMIT)锁定 60 秒(SB_LOCKOUT_TIME),脚本重试别打满。

Q3:宿主侧看到数据目录文件属主是 root,服务进程也是 root,有风险吗? 入口脚本按 /data 属主身份运行服务(本文实验目录属主是 root,故 UID 0)。生产建议:mkdir -p /opt/silverbullet/data && chown -R 1000:1000 /opt/silverbullet/data,服务即以 UID 1000 运行;或在编排里显式 PUID/PGID。权限与「页面即文件」直接相关——服务进程 UID 决定它写出的文件属主,保持宿主操作账号与服务 UID 一致可避免「页面上改不了、宿主删不掉」的混局。

Q4:docker compose up -d 拉镜像卡在层下载不动? 本文实测即遇到:GHCR 通道 manifest 秒回、层下载长时间零进展。处理顺序:先确认磁盘空间与网络;再换官方 Docker Hub 同名仓库(zefhemel/silverbullet,同一镜像同一层摘要,本文经镜像加速器拉取成功,回显见第二节);也可改用 -slim 变体减小下载量(约省下 Chromium 那 260 MB 的层,代价是运行时 API 不可用)。镜像到手后按第二节命令补打 GHCR 标签,编排文件无需改动。

Q5:上了反代/HTTPS 后页面能开但实时更新失效、连接莫名卡死? 两个官方明示的反代坑:一是没开 HTTP/2,6 个并发连接被同步引擎占满,客户端报「Could not reach remote server, we're offline or the server is down」;二是反代缓冲了 /.events SSE 事件流,实时推送退化。修法见「十一、HTTPS 前置说明」末尾两条硬性注意,配置后用 curl -w '%{http_version}' 验证协议版本。

Q6:升级后页面显示的还是旧版界面/行为? 客户端静态资源有浏览器缓存,官方要求升级后刷新两次页面;仍异常时用无痕窗口验证。服务端版本以启动日志的版本横幅为准(SilverBullet 2.12.1-0-g4d5eac53-…)。

十三、参考链接