Koha 26.05 Docker Compose 部署指南
基于官方 koha-docker 多服务编排实测 Koha 26.05 的 Docker Compose 部署:compose 文件与 .env 逐行说明、init 与 web installer 全流程、OPAC/后台/ES 验证、持久化与运维命令,附 5 条实测排障实录。
Koha 26.05 Docker Compose 部署指南
目标:用官方 koha-docker 多服务编排完成 Koha 26.05 的端到端部署与初始化——compose 起栈 → init 建实例 → web 安装器建库 → onboarding 建馆建超管 → OPAC/后台/Elasticsearch/REST API 验证。已在 Ubuntu(7.0.0-38-generic)+ Docker 29.9.2 + Compose v5.6.1 环境实测通过,正文命令与回显均为实测原文。
一、环境信息
本篇基于 Koha 官方容器项目 koha-docker(GitLab koha-community/docker/koha-docker,main 分支)的多服务编排,在本机完成端到端落地实测。实测环境:
| 项目 | 值 |
|---|---|
| 主机 | Linux master 7.0.0-38-generic #38-Ubuntu SMP x86_64 |
| Docker | 29.9.2 |
| Docker Compose | v5.6.1 |
| Koha 版本 | 26.05(镜像内包 koha-core 26.05.04-1,界面 About 显示 Koha 26.05.04) |
| 数据库 | mariadb:11.4(实测服务端 11.4.13-MariaDB-ubu2404) |
| 检索引擎 | koha/elasticsearch-icu:9(实测 ES 9.4.6) |
| 缓存 | memcached |
| 对外端口 | 8140→OPAC(8080)、8142→Intranet(8081) |
| 内部网络 | kohanet,显式子网 10.89.60.0/24 |
| compose 项目名 | koha-26.05-stack(实际生效名 koha-2605-stack,点号被剥离) |
二、镜像选型说明
官方镜像 koha/koha 发布在 Docker Hub,多架构(amd64+arm64),标签规则(来源:koha-docker README,查询日期 2026-11-01):
| 标签形态 | 示例 | 性质 |
|---|---|---|
| 完整版本 | 26.05.03-1 | 不可变(含 -N 修订号,可复现) |
| 补丁版本 | 26.05.03 | 滚动别名 |
| 次版本 | 26.05 | 滚动别名(本实测使用) |
stable | — | 始终指向当前稳定版(26.05) |
镜像通过 koha-core 元包安装 Koha(保留全部 koha-* 管理命令),但刻意剔除 koha-common 会拖入的 RabbitMQ、memcached、Apache、Zebra 等可独立编排的依赖——这些在栈中作为独立服务存在或不使用。koha/elasticsearch-icu:9 是官方带 ICU 分析插件的 Elasticsearch 镜像(Koha 多语言分词需要)。
三、前置条件
- Docker 与 Compose v2+(实测 v5.6.1);
- 空闲 Web 端口(实测选取 8140/8142)与自定义网络子网槽(实测 10.89.60.0/24);
- 内存 ≥ 2GB(Elasticsearch 在官方 compose 中堆限
-Xms512m -Xmx512m); - 磁盘 ≥ 10GB(koha/koha:26.05 镜像约 3.7GB,ES 约 2.5GB,加数据卷)。
四、部署步骤
4.1 准备 compose 目录
在服务器上建立部署目录(本实测为 koha-docker/),放入官方 docker-compose.yml(按本地环境改四处:显式项目名、端口映射、显式子网、数据目录 bind 挂载)与 .env。核心编排如下(实测文件全文):
---
name: koha-26.05-stack
x-koha-image: &koha-image
image: koha/koha:${KOHA_VERSION:-stable}
x-koha-common: &koha-common
<<: *koha-image
env_file:
- .env
cap_add:
- ALL
volumes:
- ${INSTANCE_VOLUME:-inst-data}:/var/lib/koha:delegated
- ${ETC_VOLUME:-etc-data}:/etc/koha/sites:delegated
- apache-sites-available:/etc/apache2/sites-available
- apache-sites-enabled:/etc/apache2/sites-enabled
- koha-log:/var/log/koha
- koha-cache:/var/cache/koha
networks:
- kohanet
depends_on:
init:
condition: service_completed_successfully
services:
db:
image: mariadb:11.4
restart: always
env_file:
- .env
environment:
MARIADB_ROOT_PASSWORD: "${DB_ROOT_PASSWORD:-password}"
MARIADB_DATABASE: "${DB_NAME:-koha_${KOHA_INSTANCE}}"
MARIADB_USER: "${DB_USER:-koha_${KOHA_INSTANCE}}"
MARIADB_PASSWORD: "${DB_PASSWORD:-password}"
volumes:
- ${DB_VOLUME:-db-data}:/var/lib/mysql:delegated
networks:
- kohanet
healthcheck:
test: ["CMD-SHELL", "mariadb --user=${DB_USER:-root} --password=${DB_PASSWORD} --host=${DB_SERVER:-db} koha_${KOHA_INSTANCE} -e 'select 1' || mysql --user=${DB_USER:-root} --password=${DB_PASSWORD} --host=${DB_SERVER:-db} koha_${KOHA_INSTANCE} -e 'select 1'"]
interval: 10s
start_period: "2m"
start_interval: "2s"
es:
image: koha/elasticsearch-icu:9
profiles: ["es"]
environment:
- cluster.name=docker-cluster
- discovery.type=single-node
- bootstrap.memory_lock=true
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
networks:
- kohanet
healthcheck:
test: ["CMD", "curl", "-f", "127.0.0.1:9200"]
interval: 10s
start_period: "2m"
start_interval: "2s"
memcached:
image: memcached
command: memcached -m 64m
networks:
- kohanet
healthcheck:
test: ["CMD-SHELL", "/bin/bash -c 'exec 3<>/dev/tcp/localhost/11211; printf \"stats\nquit\n\" >&3'"]
interval: 10s
start_period: "2m"
start_interval: "5s"
init:
<<: *koha-image
env_file:
- .env
cap_add:
- ALL
restart: "no"
environment:
KOHA_SERVICE: init
volumes:
- ${INSTANCE_VOLUME:-inst-data}:/var/lib/koha:delegated
- ${ETC_VOLUME:-etc-data}:/etc/koha/sites:delegated
- apache-sites-available:/etc/apache2/sites-available
- apache-sites-enabled:/etc/apache2/sites-enabled
- koha-log:/var/log/koha
- koha-cache:/var/cache/koha
networks:
- kohanet
depends_on:
db:
condition: service_healthy
memcached:
condition: service_healthy
plack:
<<: *koha-common
restart: always
environment:
KOHA_SERVICE: plack
networks:
kohanet:
aliases:
- koha
tmpfs:
- /tmp
ulimits:
nofile:
soft: 65536
hard: 65536
apache:
<<: *koha-common
restart: always
environment:
KOHA_SERVICE: apache
depends_on:
init:
condition: service_completed_successfully
plack:
condition: service_started
networks:
kohanet:
aliases:
- "${KOHA_INTRANET_PREFIX}${KOHA_INSTANCE}${KOHA_INTRANET_SUFFIX}${KOHA_DOMAIN}"
- "${KOHA_OPAC_PREFIX}${KOHA_INSTANCE}${KOHA_OPAC_SUFFIX}${KOHA_DOMAIN}"
ports:
- "8140:8080"
- "8142:8081"
cron:
<<: *koha-common
restart: always
environment:
KOHA_SERVICE: cron
worker-default:
<<: *koha-common
restart: always
environment:
KOHA_SERVICE: worker
WORKER_QUEUE: default
WORKER_MAX_PROCESSES: ${WORKER_MAX_PROCESSES:-1}
worker-long-tasks:
<<: *koha-common
restart: always
environment:
KOHA_SERVICE: worker
WORKER_QUEUE: long_tasks
WORKER_MAX_PROCESSES: ${WORKER_MAX_PROCESSES:-1}
es-indexer:
<<: *koha-common
restart: always
profiles: ["es"]
environment:
KOHA_SERVICE: es-indexer
volumes:
apache-sites-available:
apache-sites-enabled:
koha-log:
koha-cache:
networks:
kohanet:
driver: bridge
ipam:
config:
- subnet: 10.89.60.0/24
设计要点:同一 koha/koha 镜像复用于全部应用服务,每个服务用环境变量 KOHA_SERVICE 声明自己前台运行的唯一进程(init/plack/apache/cron/worker/es-indexer,另有 zebra/zebra-indexer/sip/z3950 角色由 profiles 控制);init 是一次性任务,完成实例创建后退出 0,其余服务通过 depends_on: service_completed_successfully 等待它,避免多进程竞态建实例。
4.2 配置 .env
实测 .env(实例名 koha、root 密码 q1w2e3r4、时区 Asia/Shanghai、检索引擎 Elasticsearch):
KOHA_VERSION=26.05
DB_VOLUME=/opt/data/quzuo/tmp/koha/koha-docker/data/db
INSTANCE_VOLUME=/opt/data/quzuo/tmp/koha/koha-docker/data/instance
ETC_VOLUME=/opt/data/quzuo/tmp/koha/koha-docker/data/etc
KOHA_DOMAIN=.myDNSname.org
KOHA_INSTANCE=koha
KOHA_INTRANET_PORT=8081
KOHA_INTRANET_PREFIX=
KOHA_INTRANET_SUFFIX=-intra
KOHA_MARC_FLAVOUR=marc21
KOHA_OPAC_PORT=8080
KOHA_OPAC_PREFIX=
KOHA_OPAC_SUFFIX=
KOHA_LANGS=
MANA_URL=https://mana-test.koha-community.org
DB_SERVER=db
DB_ROOT_PASSWORD=q1w2e3r4
DB_ROOT_USER=root
DB_NAME=koha_koha
DB_USER=koha_koha
DB_PASSWORD=q1w2e3r4
ZEBRA_MARC_FORMAT=marc21
SEARCH_ENGINE=Elasticsearch
ELASTICSEARCH_SERVER=es:9200
TIMEZONE=Asia/Shanghai
WORKER_MAX_PROCESSES=1
USE_SIP=
USE_NCIP=
4.3 启动栈
docker compose --profile es up -d
实测回显(节选,init 一次性任务完成后其余服务依次拉起):
Container koha-26.05-init Started
Container koha-26.05-init Waiting
Container koha-26.05-init Exited
Container koha-26.05-cron Starting
Container koha-26.05-worker-default Starting
Container koha-26.05-worker-longtasks Starting
Container koha-26.05-esindexer Starting
Container koha-26.05-plack Starting
Container koha-26.05-cron Started
...
Container koha-26.05-apache Starting
Container koha-26.05-apache Started
查看一次性初始化日志:
docker compose logs init
实测关键回显(实例创建、时区、Plack 启用):
Koha ILS in Docker
Koha version : 26.05.04-1
Base image : Debian GNU/Linux 12 (bookworm) (amd64)
Instance : koha
Role : init
Search engine: Elasticsearch (configured)
("koha" is Māori for "gift")
2026-11-01T15:29:58,992510396+00:00 Initialising koha instance koha
2026-11-01T15:29:59,045005075+00:00 Creating instance koha
2026-11-01T15:29:59,101591274+00:00 Executing koha-create for instance koha
2026-11-01T15:29:59,102688911+00:00 Setting instance timezone to Asia/Shanghai
Plack enabled for koha OPAC
Plack enabled for koha Intranet
2026-11-01T15:30:00,825154331+00:00 Instance koha initialised.
回显中的 Error: Indexer not running for koha: failed! 为已知噪音(见「常见问题」10.3)。
4.4 完成 web 安装器
实例创建后业务库表尚未导入,Intranet 站点会强制跳转安装器。实测:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://127.0.0.1:8142/
# 302 http://127.0.0.1:8142/cgi-bin/koha/installer/install.pl
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://127.0.0.1:8140/
# 302 http://127.0.0.1:8140/cgi-bin/koha/maintenance.pl
浏览器访问 http://<服务器IP>:8142/(vhost 对 IP 直连兜底可用,无需改 hosts),按安装器向导操作:
- 登录:安装器要求输入「koha-conf.xml 中记录的凭据」,即实例数据库用户——本例
koha_koha/q1w2e3r4(.env中DB_USER/DB_PASSWORD)。实测登录页标题Welcome to the Koha 26.05 web installer; - Check Perl dependencies:官方镜像预装全部依赖,页面显示
All dependencies installed.,点 Continue; - Set up database:导入表结构,页面返回
Database tables created。实测导入后 MariaDB 内表数量:
docker compose exec -T db mariadb -uroot -pq1w2e3r4 koha_koha \
-e "select count(*) as tables_cnt from information_schema.tables where table_schema='koha_koha';"
# tables_cnt
# 295
- MARC 框架选择:勾选保持默认(MARC21 默认框架、权威框架、sysprefs/userflags 等 13 项),页面返回
Default data loaded; - 安装完成后进入 onboarding 向导(26.05 起建馆/建管理员从安装器拆出):
- Create a library:代码
CTR,名称Central Library; - Create a patron category:代码
STAFF,类型 Staff; - Create Koha administrator patron:用户
root,密码Q1w2e3r4(注意大小写,见「常见问题」10.1),权限 superlibrarian; - Create a new item type:
BOOK/ Books。 每步页面回显Administrator account created!、New item type created!。
- Create a library:代码
4.5 验证
docker compose ps
实测回显:
koha-26.05-apache koha/koha:26.05 Up 17 minutes 0.0.0.0:8140->8080/tcp, [::]:8140->8080/tcp, 0.0.0.0:8142->8081/tcp, [::]:8142->8081/tcp
koha-26.05-cron koha/koha:26.05 Up 17 minutes 8080-8081/tcp
koha-26.05-db mariadb:11.4 Up 17 minutes (healthy) 3306/tcp
koha-26.05-es koha/elasticsearch-icu:9 Up 17 minutes (healthy) 9200/tcp, 9300/tcp
koha-26.05-esindexer koha/koha:26.05 Up 17 minutes 8080-8081/tcp
koha-26.05-memcached memcached Up 7 minutes (healthy) 11211/tcp
koha-26.05-plack koha/koha:26.05 Up 8 minutes 8080-8081/tcp
koha-26.05-worker-default koha/koha:26.05 Up 17 minutes 8080-8081/tcp
koha-26.05-worker-longtasks koha/koha:26.05 Up 17 minutes 8080-8081/tcp
- OPAC:
curl -s http://127.0.0.1:8140/ | grep -oE '<title>[^<]*'→Koha online catalog; - Intranet:浏览器
http://<服务器IP>:8142/用root/Q1w2e3r4登录成功,About Koha页显示Koha 26.05.04; - 检索链路:执行
docker compose exec plack koha-elasticsearch --rebuild koha后,docker compose exec es curl -s 127.0.0.1:9200/_cat/indices实测回显:
yellow open koha_koha_authorities EZmegIlbS_ilbg_623Cvwg 5 1 0 0 1.1kb 1.1kb 1.1kb
yellow open koha_koha_biblios MuTtX0kMRv2dl0PxLqDPbg 5 1 0 0 1.1kb 1.1kb 1.1kb
OPAC 搜索 q=test 返回 No results(空库,检索管线已通)。
五、数据持久化说明
| 挂载 | 路径(栈内) | 实测载体 | 内容 |
|---|---|---|---|
${DB_VOLUME} | /var/lib/mysql | bind ./data/db | MariaDB 数据目录 |
${INSTANCE_VOLUME} | /var/lib/koha | bind ./data/instance | 实例运行数据:上传、tmp、插件、koha-bootstrapped/koha-sysprefs-applied 标记 |
${ETC_VOLUME} | /etc/koha/sites | bind ./data/etc | 实例配置 koha-conf.xml、log4perl.conf、Zebra 配置 |
| apache-sites-* / koha-log / koha-cache | /etc/apache2/sites-*、/var/log/koha、/var/cache/koha | 命名卷 | vhost 共享、日志、模板缓存 |
docker compose down 不带 -v 时 bind 目录与命名卷均保留,重新 up 复用同一实例;彻底重置需 down -v 并清空 data/ 三目录。
六、环境变量表
.env(env_file 注入全部服务)关键变量(完整默认值见官方 env/defaults.env):
| 变量 | 实测值 | 作用 |
|---|---|---|
KOHA_VERSION | 26.05 | 选择 koha/koha:${KOHA_VERSION:-stable} 标签 |
KOHA_INSTANCE | koha | 实例名(数据库 koha_koha、目录 /etc/koha/sites/koha) |
KOHA_DOMAIN | .myDNSname.org | vhost 域名后缀(OPAC koha.myDNSname.org,Intranet koha-intra.myDNSname.org) |
KOHA_OPAC_PORT / KOHA_INTRANET_PORT | 8080 / 8081 | 站点内部端口 |
KOHA_MARC_FLAVOUR | marc21 | MARC 标准(marc21/unimarc) |
KOHA_LANGS | (空) | 附加安装的语言包列表 |
DB_SERVER/DB_NAME/DB_USER/DB_PASSWORD/DB_ROOT_* | db / koha_koha / koha_koha / q1w2e3r4 | 数据库连接(同时是安装器登录凭据) |
SEARCH_ENGINE | Elasticsearch | 检索引擎,须与启用的 profile(es/zebra)一致 |
ELASTICSEARCH_SERVER | es:9200 | 写入 koha-conf.xml 的 ES 地址 |
TIMEZONE | Asia/Shanghai | 写入 koha-conf.xml(勿用 $TZ,koha-conf 为权威) |
WORKER_MAX_PROCESSES | 1 | 每 worker 并发(background_jobs_worker.pl --max-processes) |
KOHA_SERVICE | 按服务设定 | 角色选择:init/plack/apache/cron/worker/es-indexer(另有 zebra/zebra-indexer/sip/z3950) |
MANA_URL | https://mana-test.koha-community.org | 共享更新源 ManaKoha |
SIP_ACCOUNTS 等 | — | SIP2 服务(profile sip)账号段 |
七、配置文件挂载
- 实例主配置
koha-conf.xml由 init 服务koha-create生成于data/etc/koha/koha-conf.xml(bind 卷内,可直接编辑,改后需重启 plack/apache 生效),逐项详解见本专栏《Koha 26.05 配置参数详解》; - Apache vhost 由 init 一次性生成到共享卷
apache-sites-available,实测koha.conf含*:8080(OPAC)与*:8081(Intranet)两个<VirtualHost>,各自SetEnv KOHA_CONF /etc/koha/sites/koha/koha-conf.xml并以AssignUserID koha-koha降权; - 日志配置
log4perl.conf与业务日志分别位于data/etc/koha/log4perl.conf与命名卷koha-log(/var/log/koha/koha/*.log)。
八、等价 docker run
Koha 应用服务依赖 compose 的 depends_on 编排与共享卷,不建议拆单起;三个基础服务可用等价单服务命令做隔离排障:
docker run -d --name koha-db-probe --network koha-2605-stack_kohanet \
-e MARIADB_ROOT_PASSWORD=q1w2e3r4 -e MARIADB_DATABASE=koha_koha \
-e MARIADB_USER=koha_koha -e MARIADB_PASSWORD=q1w2e3r4 mariadb:11.4
docker run -d --name koha-memcached-probe --network koha-2605-stack_kohanet memcached -m 64m
docker run -d --name koha-es-probe --network koha-2605-stack_kohanet \
-e discovery.type=single-node -e xpack.security.enabled=false \
-e ES_JAVA_OPTS=-Xms512m -Xmx512m koha/elasticsearch-icu:9
九、常用运维命令
docker compose ps # 服务状态
docker compose logs -f plack # 跟踪应用服务日志
docker compose restart plack # 重启 Plack(配置/参数变更生效)
docker compose --profile es up -d # 增量拉起/收敛
docker compose down # 停栈保留数据;down -v 连命名卷一起删
# 管理命令统一经 plack 服务执行(koha-* 命令族详见 CLI 篇)
docker compose exec plack koha-list
docker compose exec plack koha-shell -c "whoami" koha
docker compose exec plack koha-dump koha
docker compose exec plack koha-elasticsearch --rebuild koha
实测回显:
$ docker compose exec -T plack koha-list
koha
$ docker compose exec -T plack koha-shell -c "whoami" koha
koha-koha
$ docker compose exec plack koha-dump koha
Dumping Koha site koha:
* DB to /var/spool/koha/koha/koha-2026-11-01.sql.gz
* configs, logs to /var/spool/koha/koha/koha-2026-11-01.tar.gz
Done.
十、常见问题
10.1 onboarding 建管理员被拒:密码太弱
实测输入 q1w2e3r4 被拒,页面回显:
The patron has not been created the entered password was too weak, must contain at least one uppercase, and lower case letter and one number
Koha 26.05 安装器强制密码含大写+小写+数字。改用 Q1w2e3r4 后通过。
10.2 建管理员报「card number or username already exists」但库为空
实测排障:该报错文案有误导性,真实原因看 plack 服务日志 /var/log/koha/koha/plack-intranet-error.log:
[WARN] DBD::mysql::st execute failed: Column 'privacy' cannot be null
根因:onboarding 建读者类别时 default_privacy 传了非法值(如 999),落库时被 enum 列静默转成 'default',而 'default' 解析依赖系统参数、返回 NULL,插入 borrowers.privacy(NOT NULL)失败。修复:
docker compose exec -T db mariadb -uroot -pq1w2e3r4 koha_koha \
-e "update categories set default_privacy='forever' where categorycode='STAFF';"
重新提交建管理员表单即成功(Administrator account created!)。
10.3 init 日志与 koha-* --status 报 not running
koha-plack --status、koha-worker --status、koha-indexer --status、koha-zebra --status 实测均回显 ... not running for koha: failed!。这是拆分服务形态的预期噪音:这些状态脚本按包安装的守护进程方式找 PID,而官方容器栈每个前台进程由 compose 管理,不存在于脚本的视野。判断进程是否健康看 docker compose ps 与业务端口即可。
10.4 修改系统参数后不生效
系统参数(system preferences)经 Memcached 缓存,直接改数据库后需重启 memcached 服务(或 flush_all)并重启 plack。实测 REST API 开关 RESTBasicAuth 置 1 后仅重启 plack 仍回显 {"error":"Basic authentication disabled"},重启 memcached 后立即返回 200 JSON。
10.5 安装器登录用什么账号
安装器凭据即实例 koha-conf.xml 中的数据库 <user>/<pass>(.env 的 DB_USER/DB_PASSWORD),不是任何图书馆员账号。
参考链接
- 官方 koha-docker 仓库:https://gitlab.com/koha-community/docker/koha-docker
- koha/koha 镜像:https://hub.docker.com/r/koha/koha
- Koha 下载与版本:https://koha-community.org/download/
- 本专栏:《Koha 介绍与选型指南》(https://www.quzuo.net/document/EZ5oAd2sENzd)、《Koha 26.05 配置参数详解》、《Koha 26.05 常用 CLI 命令与工具详解》