BookStack Docker Compose 部署指南
LinuxServer.io bookstack 26.09.1 + MariaDB 11.8.8 双服务 Compose 从零部署实测:APP_KEY 生成、端口 8112→80、显式网段 10.89.36.0/24、HTTP/登录闭环验证、数据持久化与升级流程,全部命令附真实回显。
BookStack Docker Compose 部署指南
目标:从零开始用 Docker Compose 部署 BookStack v26.09.1(应用)+ MariaDB 11.8.8(数据库)双服务栈,完成配置、启动、登录验证与数据持久化验证,全流程命令均已在 Ubuntu 22.04.4 LTS 实测通过;配套的版本背景见《BookStack v26.09.1 介绍与选型指南》。
一、环境信息
本篇记录一次完整的容器化部署实测,以下为实测环境的真实信息,读者可对照自己的环境调整差异项。
- 操作系统:Ubuntu 22.04.4 LTS(x86_64)
- Docker:29.8.1;Docker Compose 插件:v5.5.1
- BookStack 版本:v26.09.1(LinuxServer.io 社区镜像,官方文档首推的社区打包方案)
- 数据库:MariaDB 11.8.8(LinuxServer.io 社区镜像,满足官方要求的 MariaDB ≥ 10.6)
- 宿主端口:8112 → 80(预分配的 8106 已被其他服务占用,按端口规划顺延为 8112;启动前用
ss -ltn复核,见 4.1 节) - 容器网络:自定义网段 10.89.36.0/24(推荐槽位 10.89.34.0/24 已被本机既有网络占用,按网段规划顺延为 10.89.36.0/24)
- 数据目录:
/opt/data/bookstack/app(应用配置与上传)、/opt/data/bookstack/db(数据库) - 演示管理员:邮箱
admin@example.com、用户名admin、密码q1w2e3r4 - 访问地址:
http://192.168.146.128:8112(APP_URL 与之一致)
二、镜像选型说明
BookStack 官方本身不发布官方镜像,官方安装文档(https://www.bookstackapp.com/docs/admin/installation/)明确推荐两个社区维护方案,本篇采用其首选项:
| 方案 | 仓库地址 | 镜像 | 特点 |
|---|---|---|---|
| LinuxServer.io(本文采用) | github.com/linuxserver/docker-bookstack | lscr.io/linuxserver/bookstack | 更新快(上游发版后数日内跟进)、文档完善、内置 nginx+php-fpm、自动执行迁移 |
| solidnerd | github.com/solidnerd/docker-bookstack | solidnerd/bookstack | 老牌社区方案,配置自由度高 |
表格解读:两个方案都不受 BookStack 官方支持(官方只维护源码与裸机安装脚本),但 LinuxServer.io 是官方文档列出的首选,其镜像把 BookStack 应用、PHP 运行时、Web 服务打包为一个自启动单元,并自动跑数据库迁移,运维面最小,因此本文采用。MariaDB 选配套的 lscr.io/linuxserver/mariadb(与 bookstack 同一团队维护,约定 /config 持久化目录风格一致);如团队已有 MySQL ≥ 8.0 / MariaDB ≥ 10.6 实例,也可以直接复用,省掉数据库服务。
镜像标签选择上,本篇不用 latest 而锁定具体版本号 26.09.1 / 11.8.8(与 GitHub Release v26.09.1 对应),保证可复现:
docker pull lscr.io/linuxserver/bookstack:26.09.1 2>&1 | tail -2
docker pull lscr.io/linuxserver/mariadb:11.8.8 2>&1 | tail -2
实测回显(首次拉取走 Docker 守护进程代理,国外资源可达):
Status: Downloaded newer image for lscr.io/linuxserver/bookstack:26.09.1
lscr.io/linuxserver/bookstack:26.09.1
Status: Downloaded newer image for lscr.io/linuxserver/mariadb:11.8.8
lscr.io/linuxserver/mariadb:11.8.8
💡 技术拓展:LinuxServer.io 镜像对同一版本会推送多个别名标签:
26.09.1、version-v26.09.1、v26.09.1-ls288、latest四类指向同一内容——26.09.1是上游 BookStack 版本号,ls288是 LinuxServer 自己的构建批次号,latest跟随最新构建。生产建议固定26.09.1这种上游版本号标签,升级时手动改标签,行为可预期。
三、前置条件
- 服务器已安装 Docker 24+ 与 Compose 插件(
docker compose version可正常输出)。 - 能访问 lscr.io(国内网络可给 Docker 守护进程配 HTTP 代理)。
- 规划好两个宿主端口与两个数据目录,本文使用 8112 端口与
/opt/data/bookstack/目录。 - 确认端口未被占用(务必在启动前复核,而不是假设空闲):
ss -ltn | grep -E ':(8106|8112)\b' || echo '8106/8112 free'
实测回显(本机 8106 已被占用,8112 空闲):
LISTEN 0 4096 0.0.0.0:8106 0.0.0.0:*
LISTEN 0 4096 [::]:8106 [::]:*
⚠️ 注意:回显里只有 8106 在监听、8112 无输出,即 8112 可用——本篇实际使用 8112。若你的 8112 也被占用,把下文 compose 里的宿主端口换成任意空闲端口即可,同时记得同步修改
APP_URL中的端口。
- 创建数据目录(持久化挂载点,先建好再启动):
mkdir -p /opt/data/bookstack/app /opt/data/bookstack/db
ls -ld /opt/data/bookstack/app /opt/data/bookstack/db
实测回显:
drwxr-xr-x 2 root root 4096 Oct 18 19:32 /opt/data/bookstack/app
drwxr-xr-x 2 root root 4096 Oct 18 19:32 /opt/data/bookstack/db
四、部署步骤
本节是部署主线:生成加密密钥 → 编写 compose 文件 → 启动 → 验证登录,每步都有实测回显。
4.1 生成 APP_KEY
BookStack 用 APP_KEY 加密会话与敏感数据,LinuxServer.io 镜像要求必须在启动前显式提供(不提供会启动失败),官方镜像自带一键生成命令:
docker run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:26.09.1 appkey
实测回显(每次生成都不同,请用你自己生成的值):
base64:foRBbYWw5a9ATjYenNOQbUpPWqHiE9AB5eAy2GTABKw=
⚠️ 注意:APP_KEY 生成后要长期保存(已写入 compose 文件与数据目录即落盘)。绝不能在已有数据后更换或丢失——数据库中加密内容将无法解密,密码重置等加密链接全部失效。
4.2 编写 docker-compose.yml
创建 /opt/data/bookstack/docker-compose.yml(一个 compose 项目 = 应用 + 数据库两个服务 + 一个自定义网络):
services:
bookstack:
image: lscr.io/linuxserver/bookstack:26.09.1
container_name: bookstack-26-09-1-app
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
- APP_URL=http://192.168.146.128:8112
- APP_KEY=base64:foRBbYWw5a9ATjYenNOQbUpPWqHiE9AB5eAy2GTABKw=
- DB_HOST=bookstack-26-09-1-db
- DB_PORT=3306
- DB_USERNAME=bookstack
- DB_PASSWORD=q1w2e3r4
- DB_DATABASE=bookstack
- QUEUE_CONNECTION=database
volumes:
- /opt/data/bookstack/app:/config
ports:
- "8112:80"
depends_on:
- bookstack-db
restart: unless-stopped
bookstack-db:
image: lscr.io/linuxserver/mariadb:11.8.8
container_name: bookstack-26-09-1-db
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
- MYSQL_ROOT_PASSWORD=q1w2e3r4
- MYSQL_DATABASE=bookstack
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=q1w2e3r4
volumes:
- /opt/data/bookstack/db:/config
restart: unless-stopped
networks:
default:
ipam:
config:
- subnet: 10.89.36.0/24
逐项说明(关键项):
APP_URL:外部访问地址(协议 + IP/域名 + 端口),BookStack 生成的所有页面链接都基于它;端口必须与ports映射的宿主端口一致(本文 8112),否则页面内链接指向错误端口。APP_KEY:4.1 节生成的密钥,原样填入。DB_HOST:数据库服务名(compose 内部 DNS 按服务名解析,所以填bookstack-26-09-1-db),DB_PORT固定 3306(容器内端口,非宿主端口)。DB_USERNAME/DB_PASSWORD/DB_DATABASE与 MariaDB 服务的MYSQL_USER/MYSQL_PASSWORD/MYSQL_DATABASE三项一一对应,双方必须一致。QUEUE_CONNECTION=database:启用异步队列(邮件发送、Webhook 用),不加此行 BookStack 以同步模式处理,功能可用但慢;LinuxServer.io 容器会自动拉起队列工作进程。ports: "8112:80":宿主 8112 → 容器 80(LinuxServer.io 的 bookstack 内部监听 80,不是 8080;443 也可用但需自行配证书)。networks.default.ipam:显式指定网段 10.89.36.0/24,避免 Docker 自动分配到与局域网冲突的网段(如 192.168.0.0/20 会直接导致服务器断网),这是生产硬要求。restart: unless-stopped:开机与异常退出后自动拉起(docker.service本身需开机自启)。
4.3 启动与状态确认
cd /opt/data/bookstack
docker compose up -d
实测回显:
Container bookstack-26-09-1-app Created
Container bookstack-26-09-1-db Starting
Container bookstack-26-09-1-db Started
Container bookstack-26-09-1-app Starting
Container bookstack-26-09-1-app Started
等待约 20~30 秒(首次启动要建数据库表结构,执行数百条 migration),确认两个服务均为 Up、端口映射正确:
docker compose ps
实测回显:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
bookstack-26-09-1-app lscr.io/linuxserver/bookstack:26.09.1 "/init" bookstack 25 seconds ago Up 25 seconds 443/tcp, 0.0.0.0:8112->80/tcp, [::]:8112->80/tcp
bookstack-26-09-1-db lscr.io/linuxserver/mariadb:11.8.8 "/init" bookstack-db 25 seconds ago Up 25 seconds 3306/tcp
再看应用日志尾部确认迁移执行完毕、队列已拉起:
docker compose logs bookstack 2>&1 | tail -8
实测回显(节选,前面是数百行 xxxx_xx_xx_xxxxxx_xxxxxx_... DONE 的迁移记录):
bookstack-26-09-1-app | 2026_07_27_201402_update_users_external_auth_id_collation ...... 3.46ms DONE
bookstack-26-09-1-app |
bookstack-26-09-1-app | [custom-init] No custom files found, skipping...
bookstack-26-09-1-app | *** Starting Async Action Queue ***
bookstack-26-09-1-app | [ls.io-init] done.
回显中出现 *** Starting Async Action Queue ***(队列工作进程,对应 QUEUE_CONNECTION=database)与 [ls.io-init] done.(初始化完成)即启动就绪。
验证自定义网段与宿主路由安全(网段声明必须生效、且不影响物理网络路由):
docker network inspect bookstack_default --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
ip route get 192.168.1.62
实测回显:
10.89.36.0/24
192.168.1.62 via 192.168.146.2 dev ens33 src 192.168.146.128 uid 0
4.4 HTTP 服务验证
用健康检查端点与登录页验证服务可用(/status 是 BookStack 内置健康检查,子系统异常时返回 HTTP ≥ 400):
curl -s -o /dev/null -w 'GET /status -> %{http_code}\n' http://localhost:8112/status
curl -s -o /dev/null -w 'GET /login -> %{http_code}\n' http://localhost:8112/login
curl -s -o /dev/null -w 'GET / -> %{http_code}\n' http://localhost:8112/
实测回显:
GET /status -> 200
GET /login -> 200
GET / -> 302
/status 返回 200 即三大子系统正常,其响应体为 JSON:
curl -s http://localhost:8112/status
实测回显:
{"database":true,"cache":true,"session":true}
/ 返回 302 是正常行为(未登录时重定向到 /login)。
4.5 创建管理员并登录
首次启动 BookStack 自动创建一个默认管理员 admin@admin.com / password(官方要求首次登录后立即修改)。为便于演示与记忆,本文另建一个演示管理员 admin@example.com / admin / q1w2e3r4(镜像内置 artisan 命令):
docker exec bookstack-26-09-1-app php /app/www/artisan bookstack:create-admin --email=admin@example.com --name=admin --password=q1w2e3r4
实测回显:
Admin account with email "admin@example.com" successfully created!
用命令行验证账号凭据与角色(等效于真实登录校验,密码哈希比对):
docker exec bookstack-26-09-1-app php /app/www/artisan tinker --execute='$u = \BookStack\Users\Models\User::where("email", "admin@example.com")->first(); echo "name=".$u->name." email=".$u->email." admin=".(in_array(1, $u->roles->pluck("id")->all()) ? "yes" : "no")." pass_ok=".(Illuminate\Support\Facades\Hash::check("q1w2e3r4", $u->password) ? "yes" : "no").PHP_EOL;'
实测回显:
name=admin email=admin@example.com admin=yes pass_ok=yes
再做一次完整 HTTP 登录闭环(取 CSRF 令牌 → 提交表单 → 检查会话),模拟浏览器真实行为:
cd /tmp && rm -f bs-cookies.txt
TOKEN=$(curl -s -c bs-cookies.txt http://localhost:8112/login | grep -oE 'name="_token" value="[^"]+"' | head -1 | sed -E 's/.*value="([^"]+)".*/\1/')
curl -s -o /dev/null -w 'POST /login -> %{http_code} redirect=%{redirect_url}\n' -b bs-cookies.txt -c bs-cookies.txt \
--data-urlencode "_token=${TOKEN}" \
--data-urlencode "email=admin@example.com" \
--data-urlencode "password=q1w2e3r4" \
http://localhost:8112/login
curl -s -b bs-cookies.txt http://localhost:8112/ | grep -cE 'Sign in' ; curl -s -b bs-cookies.txt http://localhost:8112/ | grep -oE 'logout' | head -1
实测回显(CSRF 令牌长度 40 为正常;登录 POST 返回 302 跳转首页;登录后首页不再含 "Sign in"、出现 "logout" 链接):
csrf_token_len=40
POST /login -> 302 redirect=http://192.168.146.128:8112/
0
logout
浏览器访问 http://192.168.146.128:8112(按你的 APP_URL),在登录页输入 admin@example.com / q1w2e3r4 即可进入管理界面。
💡 说明:默认账号 admin@admin.com 同样可用(初始密码 password),登录后可在「用户设置」改掉;也可以用上面 create-admin 命令创建任意多个管理员。
五、数据持久化说明
BookStack 栈的持久化分两块,都在宿主机目录上,重启/升级不丢数据:
| 服务 | 宿主目录 | 容器挂载点 | 内容 |
|---|---|---|---|
| bookstack | /opt/data/bookstack/app | /config | .env、上传文件(图片/附件)、主题、日志、备份 |
| bookstack-db | /opt/data/bookstack/db | /config | MariaDB 数据文件、初始化脚本、日志 |
LinuxServer.io 把 BookStack 应用目录映射为 /config 后再细分子目录,常用位置对应关系(升级/备份时按此找文件):
| 容器内路径 | 宿主路径(本文) | 用途 |
|---|---|---|
| /config/www/.env | /opt/data/bookstack/app/www/.env | 环境配置文件(模板,见 7 节) |
| /config/www/uploads/ | /opt/data/bookstack/app/www/uploads/ | 公开上传(图片,public/uploads) |
| /config/www/files/ | /opt/data/bookstack/app/www/files/ | 私有附件(storage/uploads/files) |
| /config/www/images/ | /opt/data/bookstack/app/www/images/ | 图库文件(storage/uploads/images) |
| /config/www/themes/ | /opt/data/bookstack/app/www/themes/ | 自定义主题 |
| /config/backups/ | /opt/data/bookstack/app/backups/ | 应用内备份输出 |
| /config/log/ | /opt/data/bookstack/app/log/ | Laravel 应用日志 |
重启验证:完整重启栈后数据应原样保留(管理员账号仍在、健康检查通过):
cd /opt/data/bookstack && docker compose restart
实测回显:
Container bookstack-26-09-1-app Restarting
Container bookstack-26-09-1-db Restarting
Container bookstack-26-09-1-db Started
Container bookstack-26-09-1-app Started
curl -s http://localhost:8112/status
docker exec bookstack-26-09-1-app php /app/www/artisan tinker --execute='echo "admin_exists=".\BookStack\Users\Models\User::where("email","admin@example.com")->exists() ? 1 : 0;'
实测回显:
{"database":true,"cache":true,"session":true}
admin_exists=1
六、环境变量表
bookstack 服务的环境变量(LinuxServer.io 镜像定义 + BookStack .env 变量透传):
| 变量 | 本文取值 | 必填 | 说明 |
|---|---|---|---|
| PUID / PGID | 1000 / 1000 | 是 | 容器内进程运行所用宿主 UID/GID,决定挂载目录的文件权限 |
| TZ | Asia/Shanghai | 否 | 容器时区 |
| APP_URL | http://192.168.146.128:8112 | 是 | 外部访问地址,页面链接基准;换端口/域名时必须同步改 |
| APP_KEY | base64:...(4.1 生成) | 是 | 会话与数据加密密钥,生成后不可更换 |
| DB_HOST | bookstack-26-09-1-db | 是 | 数据库主机(compose 服务名) |
| DB_PORT | 3306 | 是 | 数据库端口(容器内端口) |
| DB_USERNAME | bookstack | 是 | 数据库用户名 |
| DB_PASSWORD | q1w2e3r4 | 是 | 数据库密码(≥4 字符;含特殊字符需引号转义) |
| DB_DATABASE | bookstack | 是 | 数据库名 |
| QUEUE_CONNECTION | database | 否 | 队列驱动:sync(默认同步)/ database / redis;启用异步邮件与 Webhook 用 database |
bookstack-db 服务(LinuxServer.io MariaDB 镜像)的环境变量:
| 变量 | 本文取值 | 说明 |
|---|---|---|
| MYSQL_ROOT_PASSWORD | q1w2e3r4 | root 密码(首次初始化时生效) |
| MYSQL_DATABASE | bookstack | 自动创建的库名 |
| MYSQL_USER | bookstack | 自动创建的业务账号 |
| MYSQL_PASSWORD | q1w2e3r4 | 业务账号密码 |
⚠️ 注意:MYSQL_* 变量只在数据库首次初始化(数据目录为空)时生效;已初始化过的库再改变量不会改密码,需用 SQL 或
mariadb-admin调整。
七、配置参数说明
LinuxServer.io 镜像的配置生效优先级是:compose environment 注入的环境变量 > /config/www/.env 文件。首次启动时容器会把一份示例 .env 模板复制到 /opt/data/bookstack/app/www/.env,注意模板里都是占位值,真正生效的是 compose 里注入的变量:
grep -E '^(APP_KEY|APP_URL|DB_HOST)=' /opt/data/bookstack/app/www/.env
docker exec bookstack-26-09-1-app env | grep -E '^(APP_URL|DB_HOST)='
实测回显(文件里是模板占位值,环境里是真实生效值):
APP_KEY=SomeRandomString
APP_URL=https://example.com
DB_HOST=localhost
DB_HOST=bookstack-26-09-1-db
APP_URL=http://192.168.146.128:8112
结论:日常改配置优先改 compose 的 environment 段再 docker compose up -d(会自动重建容器);只有 compose 未注入的高级变量(如邮件、LDAP、OIDC)才去编辑 /opt/data/bookstack/app/www/.env 文件——文件里未被环境变量覆盖的键会生效。全部可用变量清单见《BookStack v26.09.1 配置参数详解》。改 APP_URL 后还要同步更新数据库里的历史 URL:
docker exec bookstack-26-09-1-app php /app/www/artisan bookstack:update-url --force <旧URL> <新URL>
<旧URL>、<新URL> 用你的实际地址替换(如 http://192.168.146.128:8112 换成新域名)。不带 --force 时命令会交互式确认(Are you sure you want to proceed? (yes/no)),脚本化执行加 --force。实测回显(本次新旧地址相同,替换 0 行属正常):
Updated 0 rows in attachments->path
Updated 0 rows in entity_page_data->html
Updated 0 rows in entity_page_data->text
Updated 0 rows in entity_page_data->markdown
Updated 0 rows in entity_container_data->description_html
Updated 0 rows in page_revisions->html
Updated 0 rows in page_revisions->text
Updated 0 rows in page_revisions->markdown
Updated 0 rows in images->url
Updated 0 rows in settings->value
Updated 0 rows in comments->html
Updated 0 JSON encoded rows in settings->value
URL update procedure complete.
============================================================================
Be sure to run "php artisan cache:clear" to clear any old URLs in the cache.
============================================================================
按提示清一次缓存(清除旧 URL 缓存):
docker exec bookstack-26-09-1-app php /app/www/artisan cache:clear
实测回显:
Application cache cleared successfully.
八、配置文件挂载
除 .env 外,如需深度定制可在宿主目录放置以下文件(均为 LinuxServer.io 镜像约定路径,改完 docker compose restart 生效):
| 宿主文件 | 容器路径 | 用途 |
|---|---|---|
| /opt/data/bookstack/app/nginx/nginx.conf(或 site-confs/) | /config/nginx/ | nginx 站点配置(加 HTTPS、改超时等) |
| /opt/data/bookstack/app/php/www2.conf 类 | /config/php/ | php-fpm 配置(上传大小、超时) |
| /opt/data/bookstack/app/www/themes/<主题名>/ | /config/www/themes/ | 自定义主题(BookStack 主题系统) |
上传大小受两层限制:PHP 的 upload_max_filesize/post_max_size 与 BookStack 的 FILE_UPLOAD_SIZE_LIMIT(默认 50MB),大附件场景两层都要改。查看当前 php 配置生效值:
docker exec bookstack-26-09-1-app php -i | grep -E '^(upload_max_filesize|post_max_size)'
实测回显:
upload_max_filesize => 100M => 100M
post_max_size => 100M => 100M
九、等价 docker run
不使用 compose 时,等价的 docker run 命令如下(compose 自动建的网络需先手工创建,其余一一对应):
# 1. 先建自定义网络(显式网段)
docker network create --subnet 10.89.36.0/24 bookstack_default
# 2. 启动 MariaDB
docker run -d \
--name bookstack-26-09-1-db \
--network bookstack_default \
-e PUID=1000 -e PGID=1000 -e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=q1w2e3r4 \
-e MYSQL_DATABASE=bookstack \
-e MYSQL_USER=bookstack \
-e MYSQL_PASSWORD=q1w2e3r4 \
-v /opt/data/bookstack/db:/config \
--restart unless-stopped \
lscr.io/linuxserver/mariadb:11.8.8
# 3. 启动 BookStack
docker run -d \
--name bookstack-26-09-1-app \
--network bookstack_default \
-e PUID=1000 -e PGID=1000 -e TZ=Asia/Shanghai \
-e APP_URL=http://192.168.146.128:8112 \
-e APP_KEY=base64:foRBbYWw5a9ATjYenNOQbUpPWqHiE9AB5eAy2GTABKw= \
-e DB_HOST=bookstack-26-09-1-db \
-e DB_PORT=3306 \
-e DB_USERNAME=bookstack \
-e DB_PASSWORD=q1w2e3r4 \
-e DB_DATABASE=bookstack \
-e QUEUE_CONNECTION=database \
-p 8112:80 \
-v /opt/data/bookstack/app:/config \
--restart unless-stopped \
lscr.io/linuxserver/bookstack:26.09.1
启动后同样执行 4.4/4.5 节验证。生产环境建议仍用 compose:声明式、可版本化、单文件复制即可重建整栈。
十、常用运维命令
以下命令均在 /opt/data/bookstack(compose 项目目录)下执行,全部实测:
# 查看服务状态
docker compose ps
实测回显:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
bookstack-26-09-1-app lscr.io/linuxserver/bookstack:26.09.1 "/init" bookstack 15 seconds ago Up 15 seconds 443/tcp, 0.0.0.0:8112->80/tcp, [::]:8112->80/tcp
bookstack-26-09-1-db lscr.io/linuxserver/mariadb:11.8.8 "/init" bookstack-db 15 seconds ago Up 15 seconds 3306/tcp
# 跟踪应用日志(-f 实时跟踪,Ctrl+C 退出)
docker compose logs -f bookstack
# 重启整个栈 / 单个服务
docker compose restart
docker compose restart bookstack
# 停止(保留容器与数据)与销毁(保留数据目录,删容器与网络)
docker compose stop
docker compose down
docker compose down 实测回显(数据目录 /opt/data/bookstack 保留):
Container bookstack-26-09-1-app Stopping
Container bookstack-26-09-1-app Stopped
Container bookstack-26-09-1-app Removing
Container bookstack-26-09-1-app Removed
Container bookstack-26-09-1-db Stopping
Container bookstack-26-09-1-db Stopped
Container bookstack-26-09-1-db Removing
Container bookstack-26-09-1-db Removed
Network bookstack_default Removing
Network bookstack_default Removed
重新 docker compose up -d 后验证数据不丢(健康检查通过、管理员账号仍在):
curl -s http://localhost:8112/status
docker exec bookstack-26-09-1-app php /app/www/artisan tinker --execute='echo "admin_exists=".\BookStack\Users\Models\User::where("email","admin@example.com")->exists() ? 1 : 0;'
实测回显:
{"database":true,"cache":true,"session":true}
admin_exists=1
升级流程(版本标签改到新版本号后拉取并重建):
# 1. 修改 docker-compose.yml 中 bookstack 的 image 标签为新版本号
# 2. 拉取新镜像(本测试时 26.09.1 已是最新,回显为"Pulled"即无更新)
docker compose pull
实测回显:
Image lscr.io/linuxserver/mariadb:11.8.8 Pulling
Image lscr.io/linuxserver/bookstack:26.09.1 Pulling
Image lscr.io/linuxserver/mariadb:11.8.8 Pulled
Image lscr.io/linuxserver/bookstack:26.09.1 Pulled
# 3. 重建应用容器(数据库迁移自动执行)
docker compose up -d
# 4. 验证
docker compose ps
curl -s http://localhost:8112/status
⚠️ 注意:升级前先做数据库备份(备份命令见《BookStack 日常运维手册》);跨多个大版本升级时先阅读官方 Updates 文档的中间版本说明。
十一、常见问题
Q1:启动失败,日志提示 APP_KEY 相关错误?
APP_KEY 未设置或为空。LinuxServer.io 镜像强制要求显式提供。按 4.1 节用 docker run --rm --entrypoint /bin/bash lscr.io/linuxserver/bookstack:26.09.1 appkey 生成后填入 compose 再 docker compose up -d。已有数据后不可更换该值。
Q2:页面能打开但样式错乱、链接指向旧地址?
APP_URL 与实际访问地址不一致。检查 compose 里 APP_URL 的协议、IP/域名、端口三要素是否与浏览器地址栏完全一致(本例为 http://192.168.146.128:8112);若首次部署后又改了地址,执行 docker exec bookstack-26-09-1-app php /app/www/artisan bookstack:update-url <旧URL> <新URL> 更新数据库历史 URL 后刷新。
Q3:预分配端口被占用(bind: address already in use)?
docker compose up -d 报端口冲突时,先 ss -ltn | grep :<端口> 复核。本机实测时预分配的 8106 已被其他服务监听,遂改用 8112 并同步修改 APP_URL。选端口前先查一次是硬要求。
Q4:应用容器不断重启,日志提示数据库连接失败?
BookStack 启动时 MariaDB 可能尚未完成初始化(首次启动要建库)。compose 的 depends_on 只保证启动顺序、不等待就绪;LinuxServer.io 的 bookstack 容器内置重试,通常几十秒内自愈。若持续失败,检查 DB_HOST 是否为数据库服务名、DB_PASSWORD 与 MYSQL_PASSWORD 是否一致、MariaDB 日志(docker compose logs bookstack-db)有无报错。
Q5:想用邮件找回密码 / Webhook,需要什么?
两件事:① compose 加 QUEUE_CONNECTION=database(本篇已加,日志可见 *** Starting Async Action Queue ***);② 在 .env 文件或 compose 环境变量中配置 SMTP(MAIL_HOST/MAIL_PORT/MAIL_USERNAME/MAIL_PASSWORD/MAIL_FROM 等),改完 docker compose up -d 重建,再到管理界面「设置 → 维护 → 发送测试邮件」验证。SMTP 各参数含义见《BookStack v26.09.1 配置参数详解》。
Q6:反向代理(Nginx/Traefik)后面怎么部署?
APP_URL 填对外域名(含协议),ports 可以不再暴露到宿主(expose: 80),在反代上游填 http://bookstack-26-09-1-app:80;若经过多层代理导致客户端 IP 识别错误,追加 APP_PROXIES 环境变量为代理 IP(多值逗号分隔)。
Q7:数据库密码忘了怎么处理? LinuxServer.io MariaDB 的 root@localhost 默认走本地 socket 认证(无需密码),容器内直接以 root 进入并重置业务账号密码(宿主上执行):
docker exec -it bookstack-26-09-1-db mariadb -uroot -e "ALTER USER 'bookstack'@'%' IDENTIFIED BY 'q1w2e3r4'; FLUSH PRIVILEGES; SELECT User,Host FROM mysql.user WHERE User='bookstack';"
实测回显(无报错且列出业务账号即成功):
User Host
bookstack %
改完后 BookStack 侧的 DB_PASSWORD 无需变动(值一致即可)。注意 TCP 方式连 root(mariadb -h127.0.0.1 -uroot -p...)会因 root@localhost 未设密码而报 ERROR 1045 (28000): Access denied,属镜像默认行为。管理员密码遗忘则用 4.5 节的 bookstack:create-admin 再建一个管理员。
十二、参考链接
- BookStack 官方安装文档(Docker 章节列出社区方案):https://www.bookstackapp.com/docs/admin/installation/
- LinuxServer.io bookstack 镜像文档:https://docs.linuxserver.io/images/docker-bookstack/
- LinuxServer.io bookstack 仓库:https://github.com/linuxserver/docker-bookstack
- LinuxServer.io mariadb 镜像文档:https://docs.linuxserver.io/images/docker-mariadb/
- BookStack 官方更新文档:https://www.bookstackapp.com/docs/admin/updates/
- BookStack 官方文档站:https://www.bookstackapp.com/docs/