私有远程部署
此模式在一台 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;依赖计划备份前先迁移到过滤代理。
初始化
- 创建非 root sudo 运维用户,并在禁用 root SSH 前验证密钥登录。
- 将
deploy/private/.env.example复制到/opt/libre-webui/.env,设为0600, 生成唯一密钥,并按主机设置BLOB_QUOTA_BYTES_PER_USER。BLOB_QUOTA_RESERVATION_TTL_MS会让遗弃上传预留过期,默认一小时。 - 若启用 Work,将
DOCKER_GID设为拥有/var/run/docker.sock的数字组。 - 将隧道令牌保存为
/opt/libre-webui/secrets/tunnel-token,权限0640或更严格。 - 为完整主机名创建 Cloudflare Access 自托管应用,使用 24 小时会话,只允许预期身份。
在隧道路由启用 Protect with Access。公共健康检查应使用仅限
/health/live的独立政策。不要添加全面 Bypass,它会击败 Allow。 - 保持
ENABLE_SIGNUP=false。Access 白名单保护主机名后创建第一个本地管理员; 空数据库会自动允许这一个引导账户。以后仅在明确注册窗口启用。 - 配置 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_DIR、LIBRE_WEBUI_BACKUP_RETENTION_DAYS、
LIBRE_WEBUI_CONTAINER_NAME 和 LIBRE_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.json 和 secrets.json。绝不重接或启动活动栈。检查配置,
明确更新部署值,并用隔离栈测试。
Ollama 模型可重新拉取。Docker Work 卷、Kubernetes PVC 和主机绑定目录位于应用 数据目录外,需要独立协调快照和保留政策。
更新
即使镜像标签可变,Libre WebUI 仍有状态。基础 Compose 永久将应用排除在 Watchtower 外。只能作为协调运维操作升级:
- 记录运行镜像 ID,将审核后的替代解析为不可变 digest。
- 运行
libre-webui recovery-check,启动备份服务,要求新归档和验证报告。 - 将
LIBRE_WEBUI_IMAGE设为审核 digest,拉取并只重建libre-webui;不要删除或 重建数据卷。 - 要求
/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,但仍需同样备份门禁的手动升级。
绝不要把私有单机栈连接到团队持久化服务,应部署完整团队拓扑。