Otter Wiki Docker Compose 部署指南
Otter Wiki 2.26.1 Docker Compose 从零部署实录:镜像标签选型与摘要核对、编排文件逐项说明、首启初始化与管理员注册、app-data 三件套持久化与备份、核心环境变量与配置优先级实测、升级运维与 6 个高频 FAQ 排障。
Otter Wiki Docker Compose 部署指南
目标:在一台 Linux 服务器上用 Docker Compose 从零部署一套 Otter Wiki 2.26.1(Python/Flask、数据存 git 仓库的 Markdown Wiki),覆盖镜像选型、编排文件、首启初始化、管理员注册、数据持久化与备份、核心环境变量、升级与常用运维命令。全部命令与回显已在下述环境逐条实测通过。
一、环境信息
- 操作系统:Ubuntu 26.04.1 LTS(x86_64,内核 7.0.0-38-generic)
- Docker:29.9.2;Docker Compose:v5.6.1(v2 命令行语法)
- Otter Wiki:2.26.1(GitHub Releases 最新稳定版,官方镜像
redimp/otterwiki:2,构建标识v2.26.1-0-gbb1aef4,2026-10-29 实查) - 部署形态:Docker Compose(Otter Wiki 官方唯一主推的部署方式,官方文档 Installation 页以 docker-compose.yml 为标准路径)
- 本次实测部署参数:项目目录
/opt/otterwiki/,Compose 项目名otterwiki-2.26.1-stack,宿主端口8137→ 服务内80,数据目录/opt/otterwiki/app-data/ - 演示账号:
admin@otterwiki.local/q1w2e3r4 - 官方文档实查日期:2026-10-29(otterwiki.com/Installation、otterwiki.com/Configuration、github.com/redimp/otterwiki README)
二、镜像选型说明
Otter Wiki 官方维护 Docker Hub 镜像 redimp/otterwiki,可用标签分三类,选型前先弄清浮动与固定的差别:
| 标签 | 含义 | 适用场景 |
|---|---|---|
2 | 跟随 2.x 最新版的浮动标签 | 官方 compose 示例默认;升级只需 pull |
2.26.1 | 精确版本固定标签 | 生产锁定版本、可复现部署 |
latest | 跟随最新发布(含未来大版本) | 不建议,大版本升级可能有破坏性变更 |
💡 技术拓展:Docker 镜像标签策略常见三种——浮动标签(
2/latest,自动获更新但版本不可控)、精确固定(2.26.1,可复现但升级要改文件)、摘要固定(@sha256:...,最强不可变但可读性差)。本文实际采用官方默认的redimp/otterwiki:2并配合文中记录的版本核对命令,兼顾官方路径与版本可追溯。
镜像元数据与标签一致性(2026-10-29 实测):
docker images --digests redimp/otterwiki --format '{{.Tag}}\t{{.ID}}\t{{.Digest}}'
实测回显:
2 8728fb977194 sha256:8728fb97719414be4790bf8e9eabc345d794876dcdf55dddd164fc9eb8e0cbcb
2.26.1 8728fb977194 sha256:8728fb97719414be4790bf8e9eabc345d794876dcdf55dddd164fc9eb8e0cbcb
:2 与 :2.26.1 镜像 ID、仓库摘要完全一致(约 680 MB,amd64/linux),即当日 :2 浮动标签恰好指向 2.26.1。镜像内部组件可通过启动日志与版本命令核实——官方镜像以内置 nginx(1.22.1)反代 uWSGI 方式运行 Flask 应用,由 supervisord 托管(PID 1),元数据声明 EXPOSE 80 8080 两个端口。
⚠️ 注意:
:2属浮动标签,上游发布新版本后重新pull会前移。生产若要求版本严格锁定,把 compose 中image:改为redimp/otterwiki:2.26.1即可,其余配置不变。
三、前置条件
- 已安装 Docker(24+ 均可)与 Docker Compose v2 命令行,验证:
docker --version && docker compose version
实测回显:
Docker version 29.9.2, build a82e528
Docker Compose version v5.6.1
- 宿主端口 8137 未被占用(可换成 8128–8152 任意空闲端口):
ss -ltn | grep 8137 || echo '8137 free'
实测回显:
8137 free
- 无需外部数据库与外部 git 服务:Otter Wiki 内置 SQLite(用户/偏好)与 git(页面版本库),数据全部落在一个数据目录。
- 无需预装 git 之外的其他组件;数据目录所在分区预留至少 1 GB。
四、部署步骤
4.1 创建项目目录与编排文件
新建项目目录并写入 docker-compose.yml。项目名通过顶层 name: 显式固定为 otterwiki-2.26.1-stack(避免多套部署共处一机时项目重名互相覆盖):
mkdir -p /opt/otterwiki && cd /opt/otterwiki
/opt/otterwiki/docker-compose.yml:
name: otterwiki-2.26.1-stack
services:
otterwiki:
image: redimp/otterwiki:2
container_name: otterwiki-2.26.1-server
restart: unless-stopped
ports:
- "8137:80"
environment:
- TZ=Asia/Shanghai
- SITE_NAME=Ops Wiki
- SITE_DESCRIPTION=Demo wiki powered by python, markdown and git
- GIT_WEB_SERVER=True
volumes:
- ./app-data:/app-data
networks:
- otterwiki-net
networks:
otterwiki-net:
driver: bridge
ipam:
config:
- subnet: 10.89.56.0/24
逐项说明:
name::Compose 项目名,决定网络/资源命名前缀,多实例隔离必配。image:官方镜像,标签策略见第二节。container_name:固定实例名,便于docker exec运维命令引用。ports "8137:80":宿主 8137 映射服务内 80(nginx 入口);换端口只改宿主侧。environment:TZ时区(影响新建提交时间显示,见十一、问题 2);SITE_NAME/SITE_DESCRIPTION站点名与副标题;GIT_WEB_SERVER=True开启 git over HTTP 远端(详见《Otter Wiki 2.26.1 常用命令与页面语法详解》)。volumes ./app-data:/app-data:数据持久化,见第五节。networks:显式自定义网桥与网段,便于多套服务做网段规划;内网无规划诉求可删去networks:两段,使用默认网络。
4.2 启动并确认健康状态
docker compose up -d
实测回显(首次部署):
Container otterwiki-2.26.1-server Creating
Container otterwiki-2.26.1-server Created
Container otterwiki-2.26.1-server Starting
Container otterwiki-2.26.1-server Started
镜像内置健康检查(访问 /-/healthz),约 10 秒后确认:
docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Ports}}'
实测回显:
NAME STATUS PORTS
otterwiki-2.26.1-server Up 15 seconds (healthy) 8080/tcp, 0.0.0.0:8137->80/tcp, [::]:8137->80/tcp
状态列为 Up ... (healthy) 即部署成功。
4.3 首次启动日志验证
docker compose logs --tail 12
实测回显(节选):
otterwiki-2.26.1-server | *** Starting An Otter Wiki 2.26.1 v2.26.1-0-gbb1aef4
otterwiki-2.26.1-server | [2026-10-29 23:16:00,042] INFO in server: server: Created initial page /home.
otterwiki-2.26.1-server | WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x721b24467af8 pid: 17 (default app)
otterwiki-2.26.1-server | 2026-10-29 23:16:01,107 INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
otterwiki-2.26.1-server | 2026-10-29 23:16:01,107 INFO success: uwsgi entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
otterwiki-2.26.1-server | 127.0.0.1 - - [29/Oct/2026:23:16:03 +0800] "GET /-/healthz HTTP/1.1" 200 2 "-" "docker-healthcheck"
三个关键信息:应用版本行(2.26.1 v2.26.1-0-gbb1aef4,与所选镜像标签一致)、Created initial page /home.(首启自动在 git 仓库中创建首页)、nginx/uWSGI 进入 RUNNING 且健康检查 200。
HTTP 层再验证一次(首页与注册页均应 200):
curl -s -o /dev/null -w 'home:%{http_code}\n' http://127.0.0.1:8137/
curl -s -o /dev/null -w 'register:%{http_code}\n' http://127.0.0.1:8137/-/register
实测回显:
home:200
register:200
4.4 注册管理员账号
浏览器访问 http://<服务器IP>:8137/,点击右上角 Register(或直达 /-/register)注册第一个账号。第一个注册的账号自动成为管理员,拥有 Settings 设置页权限。本文演示环境注册的账号为:
- 邮箱:
admin@otterwiki.local - 密码:
q1w2e3r4
注册成功后以该账号登录,右上角出现 Settings 入口即管理员身份生效。若服务器在内网无法用浏览器,也可先用默认权限(匿名可读写)直接编辑页面,再按十一、问题 1 收紧权限。
五、数据持久化说明
全部持久化数据集中在挂载进 /app-data 的一个目录(本文为 /opt/otterwiki/app-data/),首启后结构如下:
ls -la /opt/otterwiki/app-data/
实测回显:
total 60
drwxr-xr-x 3 www-data www-data 4096 Oct 29 23:16 .
drwxr-xr-x 3 root root 4096 Oct 29 23:15 ..
-rw-r--r-- 1 www-data www-data 40960 Oct 29 23:16 db.sqlite
drwxr-xr-x 3 www-data www-data 4096 Oct 29 23:16 repository
-rw-r--r-- 1 www-data www-data 188 Oct 29 23:15 settings.cfg
| 路径 | 内容 | 说明 |
|---|---|---|
/app-data/db.sqlite | SQLite 用户库 | 注册用户、偏好设置(Settings 界面保存的配置存在这里) |
/app-data/repository/ | git 版本库 | 所有页面与附件,home.md 等工作树文件 + .git/ 版本历史 |
/app-data/settings.cfg | 应用配置 | 首启自动生成,见第八节 |
版本库本身是非裸 git 仓库(工作树与 .git 同在,默认分支 main),可用宿主机 git 直接浏览版本历史:
git -C /opt/otterwiki/app-data/repository branch --show-current
git -C /opt/otterwiki/app-data/repository log --oneline
实测回显:
main
4e55fd2 Initial commit
💡 技术拓展:git 仓库分「裸仓库」(只有版本历史,无工作树,通常作远端)与「非裸仓库」(历史 + 工作树)。Otter Wiki 页面数据用非裸仓库,页面文件可直接
ls看见;其.git/config中由应用自动写入receive.denyCurrentBranch = updateInstead,使外部推送能同步更新工作树(见《Otter Wiki 2.26.1 常用命令与页面语法详解》实测)。
备份:整体打包数据目录即可(先 docker compose stop 更稳妥,避免打包时 git 正在写入):
cd /opt/otterwiki && tar czf /backup/otterwiki-$(date +%F).tar.gz app-data/
异地备份:因数据本质是 git 仓库,可用 git clone(或开启 GIT_WEB_SERVER 后 git fetch)把整个版本库克隆到远端机器,SQLite 中的用户数据另行拷贝 db.sqlite。恢复时解包 tar 到 app-data/ 再启动即可。
六、环境变量表
Otter Wiki 的可配置项约 70 个(全部键见官方 Configuration 页与《Otter Wiki 2.26.1 配置参数详解》),均可用环境变量注入。部署最常用的核心子集如下(默认值实测自官方镜像 2.26.1):
| 环境变量 | 默认值 | 说明 |
|---|---|---|
TZ | UTC | 时区;设 Asia/Shanghai 后新建提交按北京时间记录显示 |
SITE_NAME | An Otter Wiki | 站点名,显示在每页标题与邮件 |
SITE_DESCRIPTION | 无 | 站点副标题 |
SITE_LOGO | 无 | Logo 图片 URL(也可用附件路径) |
READ_ACCESS | ANONYMOUS | 读权限:ANONYMOUS / REGISTERED / APPROVED |
WRITE_ACCESS | ANONYMOUS | 写权限,取值同上 |
ATTACHMENT_ACCESS | ANONYMOUS | 附件上传权限,取值同上 |
AUTO_APPROVAL | True | 注册自动批准(配合 APPROVED 权限档) |
DISABLE_REGISTRATION | False | 关闭自助注册(账户交由管理员创建) |
GIT_WEB_SERVER | False | 开启 git over HTTP 克隆/推送端点 |
COMMIT_MESSAGE | REQUIRED | 保存页面时提交说明是否必填(REQUIRED/OPTIONAL/DISABLED) |
PUID / PGID | 33 / 33 | 数据目录属主 uid/gid(默认 www-data) |
NGINX_MAX_UPLOAD | 0 | nginx client_max_body_size,0 为不限;如 10m 限制 10 MB |
NGINX_WORKER_PROCESSES | 1 | nginx worker 数 |
⚠️ 注意:默认三个权限均为
ANONYMOUS,即匿名访客可以读写页面与上传附件,公网部署必须先收紧(见十一、问题 1)。
七、配置参数说明
同一配置项存在三个入口,生效优先级为:Settings 界面(存数据库)> 环境变量 > settings.cfg。以 SITE_NAME 为例:先按第四节以环境变量 SITE_NAME=Ops Wiki 启动,再在 Settings 界面把站点名改为其他值并保存,重启后日志会明确提示覆盖关系:
docker compose logs 2>&1 | grep -i overridden
实测回显:
[2026-10-29 23:12:57,199] WARNING in server: server: app.config["SITE_NAME"] set via environment variable is overridden by the database preference
结论:Settings 界面改过的项以数据库为准,环境变量仅对「界面从未改过」的项生效。因此推荐分工——环境变量管部署期基线(端口类、权限档、Git 开关),界面管运营期微调(站名、Logo、侧栏)。
环境变量的值类型由应用按默认值类型自动转换:布尔型接受 true/yes/on/1(大小写不敏感),整型传入非数字会被忽略并回退默认值并打 WARNING 日志。
八、配置文件挂载
/app-data/settings.cfg 在首次启动时由镜像 entrypoint 自动生成(已实测生成内容如下,SECRET_KEY 为每次安装随机生成的 64 位十六进制串):
DEBUG = False
REPOSITORY = '/app-data/repository'
SECRET_KEY = '2a177f11a9ad10d58204a807c2d5f069e72bb30d3d4273a863c107173cd82372'
SQLALCHEMY_DATABASE_URI = 'sqlite:////app-data/db.sqlite'
SECRET_KEY用于会话 Cookie 签名,随机生成且必须保密;数据目录已随 tar 备份一同保留。REPOSITORY指向 git 版本库路径,SQLALCHEMY_DATABASE_URI指向 SQLite;两者与数据目录布局对应,除非迁移数据库(如换 PostgreSQL),一般不改。- 如需手工预置配置(例如部署前就写好邮件参数),可在项目目录准备
settings.cfg并增加一行挂载./settings.cfg:/app-data/settings.cfg——文件已存在时 entrypoint 跳过生成,直接使用挂载版本。注意其优先级低于环境变量与 Settings 界面。 - 数据库切换示例(切换前备份数据目录):
SQLALCHEMY_DATABASE_URI = 'postgresql://user:pass@db-host/otterwiki'(需可达的 PostgreSQL 实例)。
九、等价 docker run
不用 Compose 的最小等价启动命令:
docker run -d --name otterwiki-2.26.1-server \
-p 8137:80 \
-e TZ=Asia/Shanghai \
-e SITE_NAME=Ops Wiki \
-v /opt/otterwiki/app-data:/app-data \
redimp/otterwiki:2
与 Compose 方案的差异:docker run 没有 name: 项目名与自定义网络编排,升级/重建全靠手工 docker stop/rm;数据目录同样挂 /app-data,两种方式可共用同一数据目录(不要同时运行两个实例指向同一目录)。除一次性验证外建议统一用 Compose。
十、常用运维命令
均在 /opt/otterwiki/(compose 文件所在目录)下执行:
# 状态与健康检查
docker compose ps
# 跟踪日志(应用/访问日志都在这里)
docker compose logs -f --tail 50
# 重启
docker compose restart
# 停止与彻底移除(数据目录保留)
docker compose down
docker compose down 实测回显:
Container otterwiki-2.26.1-server Stopped
Container otterwiki-2.26.1-server Removing
Container otterwiki-2.26.1-server Removed
Network otterwiki-2261-stack_otterwiki-net Removing
Network otterwiki-2261-stack_otterwiki-net Removed
(网络名中项目名的 . 被剥除,2.26.1 → 2261,属 Compose 资源命名规则。)
升级:浮动标签 :2 下先拉新镜像再重建实例,数据目录结构跨 2.x 兼容,升级前照例打包备份:
docker compose pull && docker compose up -d
实测回显(2026-10-29,当日已是最新,无新层下载):
Image redimp/otterwiki:2 Pulling
Image redimp/otterwiki:2 Pulled
若上游有新版本,Pulled 之后 up -d 会以新镜像重建实例,启动日志的版本行可确认生效版本。
容器内 CLI:用户管理走容器内 flask user 命令组,逐条实测见《Otter Wiki 2.26.1 常用命令与页面语法详解》。
十一、常见问题(FAQ)
Q1:部署完公网可访问,发现匿名访客能改页面?
默认三权限均为 ANONYMOUS(匿名可读写),这是 Otter Wiki 的出厂默认而非漏洞。收紧为仅注册用户可读写:在 compose 的 environment 增加三行后 docker compose up -d 重建:
- READ_ACCESS=REGISTERED
- WRITE_ACCESS=REGISTERED
- ATTACHMENT_ACCESS=REGISTERED
实测效果:匿名访问首页被 302 到登录页,匿名 git 推送被要求认证:
302 -> http://127.0.0.1:8137/-/login?next=/?
fatal: could not read Username for 'http://127.0.0.1:8137': terminal prompts disabled
Q2:Changelog 里的提交时间与本地时间对不上?
Otter Wiki 没有站点级时区设置,变更历史按每条提交自身记录的时区偏移原样显示。实测:镜像默认 UTC 时,容器内保存的页面显示为 UTC 时间(如 14:48:53);在 compose 加 TZ=Asia/Shanghai 重建后,之后容器内新建的提交即显示北京时间(实测新提交显示 23:10:30),历史提交维持各自原偏移。要让存量 UTC 时间转北京时间,只能改 git 提交数据,不建议动;实操上装好就配 TZ 即可。
Q3:改了环境变量但界面显示没变?
优先级是 Settings 界面(数据库)> 环境变量 > settings.cfg。界面上保存过的项,环境变量不再生效,启动日志会出现 overridden by the database preference 的 WARNING(见第七节回显)。处理:要么在 Settings 界面改回,要么清掉数据库里对应偏好记录(db.sqlite 的 preferences 表)。
Q4:镜像 EXPOSE 了 80 和 8080 两个端口,都要映射吗?
不用。nginx 监听 80 作为唯一对外入口;8080 是 entrypoint 自动追加的同应用直连口(listen 8080;),供容器网络内部直访调试用。官方 compose 示例也只映射 80。实测容器内监听情况:
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
Q5:多用户共用服务器,想把数据目录属主改成指定用户?
用 PUID/PGID。默认 33/33(www-data),entrypoint 启动时用 usermod/groupmod 改写镜像内 www-data 的 uid/gid 再 chown -R 数据目录。注意 PGID 不能为 0,entrypoint 会直接拒绝启动,报错原文:Fatal: PGID must not be 0 (root).。设值前确认目标 gid 未被宿主镜像内其他组占用(占用时 entrypoint 会打印 Warning: Deleting group ... 并删除该组,属预期行为)。
Q6:附件上传报 413?
未显式配置时 nginx client_max_body_size 默认 0(不限);若设置过 NGINX_MAX_UPLOAD(如 10m),超限附件会被 nginx 以 413 拒绝。按需调大该变量并重建实例即可。
十二、参考链接
- 官网与文档:https://otterwiki.com
- 安装指南(官方):https://otterwiki.com/Installation
- 配置参考(官方):https://otterwiki.com/Configuration
- 源码仓库:https://github.com/redimp/otterwiki
- Docker Hub:https://hub.docker.com/r/redimp/otterwiki
- 在线演示:https://demo.otterwiki.com
站内延伸阅读
- 《Otter Wiki 2.26.1 配置参数详解》:三层配置入口的优先级与 70+ 参数逐组详解。
- 《Otter Wiki 2.26.1 常用命令与页面语法详解》:flask user CLI、git 数据面运维与版本回滚实测。
- 《Otter Wiki 介绍与选型指南》:定位、架构与同类 Wiki 量化选型对比。