跳到主要内容

私有远程部署

此模式在一台 Docker 主机上运行 Libre WebUI、Ollama 和 Cloudflare Tunnel, 不发布应用或 Ollama 端口。Cloudflare Access 是外层身份边界;Libre WebUI 身份验证保留为内层边界。Work 和 Watchtower 是独立、等同 root 权限的可选项。

模板是单副本 solo 拓扑:SQLite、本地加密 blob、内置向量、本地协调和内置 任务 worker 共享应用数据卷。不要通过更改 .env 后端选择器将其变成团队部署。 团队必须使用 docker-compose.team.yml(启用 Work 时另加 docker-compose.team.work.yml),它将 PostgreSQL/PGVector、版本化 S3、Redis、 外部 worker 和网关作为一个协调拓扑提供。

deploy/private/docker-compose.yml 开始。默认使用 main 镜像:

LIBRE_WEBUI_IMAGE=ghcr.io/libre-webui/libre-webui:main

dev 标签只适合明确选择的开发实例,不是客户端默认值。

安全模型

  • Cloudflare Access 保护整个主机名,包括 /api/* 和 WebSocket 升级。不要添加 公共绕过路径。
  • Libre WebUI API 要求当前账户;模型生命周期和 Work 操作要求当前数据库角色为管理员。
  • 应用、Ollama、SearXNG 和 cloudflared 只使用私有 Compose 网络;主机不发布应用端口。
  • 内置 SearXNG 提供可选网页搜索,仅内部可见,管理员启用前不工作。 启动栈前在 .env 中设置 SEARXNG_SECRET
  • 应用以非 root 用户运行,根文件系统只读,无 Linux capabilities,启用 no-new-privileges,并限制 CPU、内存和 PID。
  • 未包含覆盖文件时 Work 关闭。启用后,其容器加入只读根、能力移除、资源限制、 工作区卷和默认拒绝网络政策。

基础栈不挂载 Docker socket。docker-compose.work-proxy.yml 通过内部代理持有 socket, 只转发 Work 使用的容器、镜像、卷、网络、exec 和 info;swarm、密钥、build 和 system 被拒绝,应用无需挂载或加入组。代理缩小 API 面,不缩小允许操作的爆炸半径; 能创建容器者仍可挂载主机路径。因此它是加固层,不是多租户隔离。

原始 socket 替代项仍是最大信任边界:docker-compose.work.yml 和 Watchtower 允许任意 Docker API 调用。只读挂载不会让 API 只读。内置备份辅助程序拒绝继承 原始 socket;依赖计划备份前先迁移到过滤代理。

初始化

  1. 创建非 root sudo 运维用户,并在禁用 root SSH 前验证密钥登录。
  2. deploy/private/.env.example 复制到 /opt/libre-webui/.env,设为 0600, 生成唯一密钥,并按主机设置 BLOB_QUOTA_BYTES_PER_USERBLOB_QUOTA_RESERVATION_TTL_MS 会让遗弃上传预留过期,默认一小时。
  3. 若启用 Work,将 DOCKER_GID 设为拥有 /var/run/docker.sock 的数字组。
  4. 将隧道令牌保存为 /opt/libre-webui/secrets/tunnel-token,权限 0640 或更严格。
  5. 为完整主机名创建 Cloudflare Access 自托管应用,使用 24 小时会话,只允许预期身份。 在隧道路由启用 Protect with Access。公共健康检查应使用仅限 /health/live 的独立政策。不要添加全面 Bypass,它会击败 Allow。
  6. 保持 ENABLE_SIGNUP=false。Access 白名单保护主机名后创建第一个本地管理员; 空数据库会自动允许这一个引导账户。以后仅在明确注册窗口启用。
  7. 配置 Turnstile 主机名限制,将 TURNSTILE_EXPECTED_HOSTNAME 设为准确公共主机名。

启动并验证:

cd /opt/libre-webui
docker compose config --quiet
docker compose up -d
docker compose ps

明确包含 socket 代理覆盖以启用 Work:

docker compose -f docker-compose.yml -f docker-compose.work-proxy.yml up -d

需要时仍可使用原始 socket 版本(docker-compose.work.yml),但需承担上述信任后果。

Access 激活后,命令行冒烟测试需要服务令牌,除非精确路径有窄范围绕过。凭据应避开 shell 历史,并发送两个标头:

curl --fail --silent --show-error \
-H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
-H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
https://your-hostname.example/api/auth/system-info

未认证请求必须返回 401

curl --output /dev/null --write-out '%{http_code}\n' \
-H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
-H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
https://your-hostname.example/api/work/tasks

主机加固

目录包含 sshd 片段和 fail2ban jail。应用前先在另一终端验证非 root sudo 会话, 使用 sshd -t 测试后再重载 SSH。

使用 UFW 或同等防火墙默认拒绝入站,只允许限速 SSH。本模板不发布服务端口:

ufw default deny incoming
ufw default allow outgoing
ufw limit OpenSSH
ufw enable

保持无人值守安全升级开启。没有记录需求时禁用 X11、agent 和 TCP 转发。

备份与恢复

备份前,在运行中的部署容器执行只读恢复清单,以使用准确版本、环境和挂载数据卷。 主机 checkout 中的命令可能检查错误数据库或不同代码。

docker exec libre-webui \
libre-webui recovery-check --json --data-dir /app/backend/data

退出 0 表示无阻碍,1 表示 JSON 有阻碍,2 表示命令无法运行。报告只含加密 密钥指纹和密钥存在标志,绝不打印值。与对应备份一起保存,以比较版本、架构指纹、 预期 Work 资源和排除项。

使用准确部署镜像创建专用备份加密和签名密钥。把目录放在应用卷外,并把加密密钥和 签名私钥复制到独立保护位置:

install -d -m 0700 /etc/libre-webui/backup-keys
image_ref=$(docker inspect libre-webui --format '{{.Image}}')
docker run --rm --user 0:0 --read-only --network none --cap-drop ALL \
--security-opt no-new-privileges \
--mount type=bind,src=/etc/libre-webui/backup-keys,dst=/backup-keys \
--entrypoint /usr/local/bin/libre-webui "$image_ref" \
backup keygen \
--directory /backup-keys

密钥生成拒绝现有输出。绝不要在现有备份集上生成新密钥;丢失归档密钥或签名身份会 使恢复证明失效。

安装备份和恢复脚本及 systemd 单元,然后启用计时器:

install -d -m 0700 /var/backups/libre-webui
install -m 0750 deploy/private/libre-webui-backup \
/usr/local/sbin/libre-webui-backup
install -m 0750 deploy/private/libre-webui-restore \
/usr/local/sbin/libre-webui-restore
install -m 0644 deploy/private/libre-webui-backup.{service,timer} \
/etc/systemd/system/
systemctl daemon-reload
systemctl enable --now libre-webui-backup.timer

单元可从 /etc/libre-webui/backup.env 读取仅维护覆盖,不加载应用 .env。仅在需要 时以 root 创建:

install -d -m 0750 /etc/libre-webui
install -m 0600 /dev/null /etc/libre-webui/backup.env

可设置 LIBRE_WEBUI_STACK_DIRLIBRE_WEBUI_BACKUP_RETENTION_DAYSLIBRE_WEBUI_CONTAINER_NAMELIBRE_WEBUI_BACKUP_KEY_DIR。文件保持 root 所有 和 0600。自定义密钥目录必须在 systemd 沙箱中可由 root 读取。

更改 LIBRE_WEBUI_BACKUP_DIR 也更改 systemd 写入边界。目录必须预先存在,单元需 匹配片段。例如在 backup.env 设置 LIBRE_WEBUI_BACKUP_DIR=/srv/backups/libre-webui 后:

install -d -m 0700 /srv/backups/libre-webui
systemctl edit libre-webui-backup.service

添加准确路径并重载:

[Service]
ReadWritePaths=/srv/backups/libre-webui
systemctl daemon-reload
systemctl start libre-webui-backup.service

没有匹配 ReadWritePaths= 时,ProtectSystem=strict 会正确阻止写入自定义位置。

备份服务为大型归档允许最多六小时。辅助程序获取主机锁,只在应用原本运行时停止它, 并用准确镜像针对静止卷创建归档。归档含签名清单、运维加密负载、数据目录以及打开 状态所需配置。随后独立验证完整归档再原子发布报告。只读维护容器得到私有可写 /tmp tmpfs;临时明文不留在容器层。把文件和恢复密钥复制到主机外。

使用 docker-compose.work-proxy.yml 时,恢复还必须证明数据库引用的每个 Work 卷 存在。辅助程序读取 DOCKER_HOST,定位同一 Compose 项目的代理,从真实网络附件 发现唯一共享内部网络。Compose 会加项目前缀,不要硬编码猜测名称。只有归档创建容器 加入网络且无原始 socket;独立验证使用 --network none。代理缺失、意外端点、 外部/歧义网络或原始 socket 挂载都会在停止应用和发布归档前失败。

在不替换活动卷的情况下测试恢复:

LIBRE_WEBUI_RESTORE_IMAGE="$image_ref" \
libre-webui-restore \
/var/backups/libre-webui/libre-webui-integrated-YYYYMMDDTHHMMSSZ.lwb \
libre-webui-restore-drill

恢复辅助程序拒绝现有卷或配置目标,在临时存储验证归档和清单,然后复制数据,并以 私有权限写入 runtime.jsonsecrets.json。绝不重接或启动活动栈。检查配置, 明确更新部署值,并用隔离栈测试。

Ollama 模型可重新拉取。Docker Work 卷、Kubernetes PVC 和主机绑定目录位于应用 数据目录外,需要独立协调快照和保留政策。

更新

即使镜像标签可变,Libre WebUI 仍有状态。基础 Compose 永久将应用排除在 Watchtower 外。只能作为协调运维操作升级:

  1. 记录运行镜像 ID,将审核后的替代解析为不可变 digest。
  2. 运行 libre-webui recovery-check,启动备份服务,要求新归档和验证报告。
  3. LIBRE_WEBUI_IMAGE 设为审核 digest,拉取并只重建 libre-webui;不要删除或 重建数据卷。
  4. 要求 /health/ready、登录、会话/历史、文档检索和 Work 冒烟测试通过;否则回滚, 保留失败状态和已验证备份用于诊断。

主机序列明确为手动。审核后再替换 digest,并在拉取前检查最新 .lwb/.json

docker inspect libre-webui --format '{{.Config.Image}} {{.Image}}'
docker exec libre-webui \
libre-webui recovery-check --json --data-dir /app/backend/data
systemctl start libre-webui-backup.service
systemctl --no-pager --full status libre-webui-backup.service
ls -lt /var/backups/libre-webui/libre-webui-integrated-* | head

# Set LIBRE_WEBUI_IMAGE=ghcr.io/libre-webui/libre-webui@sha256:REVIEWED_DIGEST
# in the root-owned .env, then recreate only the application.
docker compose pull libre-webui
docker compose up -d --no-deps libre-webui
docker inspect libre-webui --format '{{.State.Health.Status}} {{.Image}}'

带 socket 的 Watchtower 覆盖只用于基础文件中明确标记的 sidecar:

docker compose \
-f docker-compose.yml \
-f docker-compose.watchtower.yml \
up -d

Watchtower 每 30 分钟检查 Ollama 和 SearXNG。Ollama 模型数据留在命名卷,SearXNG 配置留在绑定挂载。它不更新 Libre WebUI、cloudflared、Work socket 代理或 Work 沙箱。客户端部署跟随 main;实验实例可选 :dev,但仍需同样备份门禁的手动升级。 绝不要把私有单机栈连接到团队持久化服务,应部署完整团队拓扑。