Koha 26.05 Docker Compose 部署指南

基于官方 koha-docker 多服务编排实测 Koha 26.05 的 Docker Compose 部署:compose 文件与 .env 逐行说明、init 与 web installer 全流程、OPAC/后台/ES 验证、持久化与运维命令,附 5 条实测排障实录。

KohaKohaDocker ComposeMariaDBElasticsearch安装部署2026/11/01

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
Docker29.9.2
Docker Composev5.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),按安装器向导操作:

  1. 登录:安装器要求输入「koha-conf.xml 中记录的凭据」,即实例数据库用户——本例 koha_koha / q1w2e3r4(.env 中 DB_USER/DB_PASSWORD)。实测登录页标题 Welcome to the Koha 26.05 web installer;
  2. Check Perl dependencies:官方镜像预装全部依赖,页面显示 All dependencies installed.,点 Continue;
  3. 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
  1. MARC 框架选择:勾选保持默认(MARC21 默认框架、权威框架、sysprefs/userflags 等 13 项),页面返回 Default data loaded;
  2. 安装完成后进入 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!。

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/mysqlbind ./data/dbMariaDB 数据目录
${INSTANCE_VOLUME}/var/lib/kohabind ./data/instance实例运行数据:上传、tmp、插件、koha-bootstrapped/koha-sysprefs-applied 标记
${ETC_VOLUME}/etc/koha/sitesbind ./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_VERSION26.05选择 koha/koha:${KOHA_VERSION:-stable} 标签
KOHA_INSTANCEkoha实例名(数据库 koha_koha、目录 /etc/koha/sites/koha)
KOHA_DOMAIN.myDNSname.orgvhost 域名后缀(OPAC koha.myDNSname.org,Intranet koha-intra.myDNSname.org)
KOHA_OPAC_PORT / KOHA_INTRANET_PORT8080 / 8081站点内部端口
KOHA_MARC_FLAVOURmarc21MARC 标准(marc21/unimarc)
KOHA_LANGS(空)附加安装的语言包列表
DB_SERVER/DB_NAME/DB_USER/DB_PASSWORD/DB_ROOT_*db / koha_koha / koha_koha / q1w2e3r4数据库连接(同时是安装器登录凭据)
SEARCH_ENGINEElasticsearch检索引擎,须与启用的 profile(es/zebra)一致
ELASTICSEARCH_SERVERes:9200写入 koha-conf.xml 的 ES 地址
TIMEZONEAsia/Shanghai写入 koha-conf.xml(勿用 $TZ,koha-conf 为权威)
WORKER_MAX_PROCESSES1每 worker 并发(background_jobs_worker.pl --max-processes)
KOHA_SERVICE按服务设定角色选择:init/plack/apache/cron/worker/es-indexer(另有 zebra/zebra-indexer/sip/z3950)
MANA_URLhttps://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 26.05 Docker Compose 部署指南 - Quzuo