Bug 40901: (QA follow-up) Document the two init packages, the new options and the logging change

- koha-systemd.README.Debian was written for the first prototype: it
  named koha-worker-long@.service (now koha-worker-long_tasks@), said
  SIP needs the sip.enabled flag file (no longer read on systemd hosts),
  described coexisting with koha-common and a manual migration that the
  postinst now performs. Rewrite it to describe the actual units, how
  the koha-* helpers map onto them, where each configuration value is
  read from, how to relax the sandboxing with a drop-in, and the
  migration.
- Logging is the one visible behavioural change and was undocumented:
  with koha-sysv the daemon(1) wrapper captured stdout/stderr to
  /var/log/koha/INSTANCE/NAME-output.log and NAME-error.log; with
  koha-systemd that output is in the journal, so zebra-*, indexer-*,
  es-indexer-*, worker-* and sip-error.log are no longer written, while
  everything Koha writes itself (Starman's plack.log/plack-error.log,
  the log4perl files, z3950.log, the Apache vhost logs) is unchanged.
  Spell that out in README.Debian, in a new koha-systemd NEWS.Debian
  entry shown by apt-listchanges on upgrade, and in a "Service
  management" section of the koha-common(8) man page.
- Add --enable/--disable to the koha-zebra, koha-worker, koha-indexer
  and koha-es-indexer man pages.
- z3950_responder.pl's synopsis listed --config-dir twice and had lost
  -c, which is still a YAZ pass-through option; put -c back.
- t/00-valid-systemd-units.t: give it the usual shebang, and ignore
  every "Command ... is not executable" line from systemd-analyze rather
  than only the ones for scripts in this repository. Whether starman or
  /usr/share/koha/bin/... is installed on the host running the test says
  nothing about the unit files, and the old filter made the test fail on
  any development machine without the Koha packages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Martin Renvoize 2026-09-15 17:40:49 +01:00 committed by Pedro Amorim
parent ffe8b55b57
commit 0aa04d9ddc
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
9 changed files with 249 additions and 77 deletions

View File

@ -25,6 +25,25 @@
<para>koha-common provides a number of scripts for creating and administering Koha instances. It is meant for sites that provide Koha hosting, and serves several sites from the same host.</para>
</refsect1>
<refsect1><title>Service management</title>
<para>Each Koha instance runs several daemons: Plack (the web application), Zebra (search) and its indexer or the Elasticsearch indexer, two background job workers, and optionally the SIP2 and Z39.50/SRU servers. koha-common is a metapackage; the daemons are supervised by whichever of these two packages it pulled in:</para>
<variablelist>
<varlistentry>
<term><option>koha-systemd</option></term>
<listitem>
<para>The default on systemd hosts. Every daemon of every instance is its own unit: <literal>koha-plack@INSTANCE.service</literal>, <literal>koha-zebra@INSTANCE.service</literal>, <literal>koha-indexer@INSTANCE.service</literal>, <literal>koha-es-indexer@INSTANCE.service</literal>, <literal>koha-worker@INSTANCE.service</literal>, <literal>koha-worker-long_tasks@INSTANCE.service</literal>, <literal>koha-sip@INSTANCE.service</literal> and <literal>koha-z3950@INSTANCE.service</literal>, grouped by <literal>koha@INSTANCE.target</literal> per instance and <literal>koha.target</literal> for the whole host. <literal>systemctl restart koha-common</literal> still restarts everything. The daemons' standard output and error go to the journal (<literal>journalctl -u koha-zebra@INSTANCE</literal>); see <filename>/usr/share/doc/koha-systemd/README.Debian</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>koha-sysv</option></term>
<listitem>
<para>The traditional <filename>/etc/init.d/koha-common</filename> script, which starts all daemons of all enabled instances through the daemon(1) wrapper. Used where systemd is not PID 1, for example in containers. Each daemon's standard output and error are captured to <filename>/var/log/koha/INSTANCE/NAME-output.log</filename> and <filename>NAME-error.log</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
<para>The koha-* commands below (koha-plack, koha-zebra, koha-worker, ...) work the same way with either package. <filename>/etc/default/koha-common</filename> holds host-wide settings such as USE_INDEXER_DAEMON and is read by both.</para>
</refsect1>
<refsect1><title>Commands</title>
<para>The following commands are available after when koha-common has been installed. See individual manual pages for each command for optios and other details related to their usage.</para>

View File

@ -24,7 +24,7 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>koha-es-indexer</command>
<arg choice="req"><option>--start</option>|<option>--stop</option>|<option>--restart</option>|<option>--status</option>|<option>--batch_size</option>|<option>--help</option>|<option>-h</option>|<option>--quiet</option>|<option>-q</option></arg>
<arg choice="req"><option>--start</option>|<option>--stop</option>|<option>--restart</option>|<option>--enable</option>|<option>--disable</option>|<option>--status</option>|<option>--batch_size</option>|<option>--help</option>|<option>-h</option>|<option>--quiet</option>|<option>-q</option></arg>
<arg choice="req" rep="repeat"><replaceable>instancename</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
@ -67,6 +67,18 @@
<para>Show the status of the ES indexer for the specified instances.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--enable</option></term>
<listitem>
<para>Enable the Elasticsearch indexing daemon for named Koha instances so it is started at boot. Only meaningful with the koha-systemd package, where it enables the corresponding systemd unit; with koha-sysv it is a no-op because the init script starts every enabled instance's daemons.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--disable</option></term>
<listitem>
<para>Disable the Elasticsearch indexing daemon for named Koha instances so it is no longer started at boot. As with <option>--enable</option>, this only has an effect with the koha-systemd package.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--quiet|-q</option></term>

View File

@ -24,7 +24,7 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>koha-indexer</command>
<arg choice="req"><option>--start</option>|<option>--stop</option>|<option>--restart</option>|<option>--status</option>|<option>--help</option>|<option>-h</option>|<option>--quiet</option>|<option>-q</option></arg>
<arg choice="req"><option>--start</option>|<option>--stop</option>|<option>--restart</option>|<option>--enable</option>|<option>--disable</option>|<option>--status</option>|<option>--help</option>|<option>-h</option>|<option>--quiet</option>|<option>-q</option></arg>
<arg choice="req" rep="repeat"><replaceable>instancename</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
@ -60,6 +60,18 @@
<para>Show the status of the indexer for the specified instances.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--enable</option></term>
<listitem>
<para>Enable the indexer daemon for named Koha instances so it is started at boot. Only meaningful with the koha-systemd package, where it enables the corresponding systemd unit; with koha-sysv it is a no-op because the init script starts every enabled instance's daemons.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--disable</option></term>
<listitem>
<para>Disable the indexer daemon for named Koha instances so it is no longer started at boot. As with <option>--enable</option>, this only has an effect with the koha-systemd package.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--debug</option></term>

View File

@ -25,6 +25,7 @@
<cmdsynopsis>
<command>koha-worker</command>
<arg><option>--start</option>|<option>--stop</option>|<option>--restart</option></arg>
<arg><option>--enable</option>|<option>--disable</option></arg>
<arg><option>--status</option></arg>
<arg><option>--queue</option></arg>
<arg><option>--all-queues</option></arg>
@ -60,6 +61,18 @@
<para>Show status information about worker daemon for named Koha instances.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--enable</option></term>
<listitem>
<para>Enable the worker daemon for named Koha instances so it is started at boot. Only meaningful with the koha-systemd package, where it enables the corresponding systemd unit; with koha-sysv it is a no-op because the init script starts every enabled instance's daemons.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--disable</option></term>
<listitem>
<para>Disable the worker daemon for named Koha instances so it is no longer started at boot. As with <option>--enable</option>, this only has an effect with the koha-systemd package.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--queue</option></term>
<listitem>

View File

@ -25,6 +25,7 @@
<cmdsynopsis>
<command>koha-zebra</command>
<arg><option>--start</option>|<option>--stop</option>|<option>--restart</option></arg>
<arg><option>--enable</option>|<option>--disable</option></arg>
<arg><option>--status</option></arg>
<arg><option>--quiet</option>|<option>-q</option></arg>
<arg><option>-h</option>|<option>--help</option></arg>
@ -58,6 +59,18 @@
<para>Show status information about Zebra daemon for named Koha instances.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--enable</option></term>
<listitem>
<para>Enable the Zebra daemon for named Koha instances so it is started at boot. Only meaningful with the koha-systemd package, where it enables the corresponding systemd unit; with koha-sysv it is a no-op because the init script starts every enabled instance's daemons.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--disable</option></term>
<listitem>
<para>Disable the Zebra daemon for named Koha instances so it is no longer started at boot. As with <option>--enable</option>, this only has an effect with the koha-systemd package.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-v</option>|<option>--verbose</option></term>
<listitem>

35
debian/koha-systemd.NEWS vendored Normal file
View File

@ -0,0 +1,35 @@
koha (26.06.00~1) unstable; urgency=medium
koha-common is now a metapackage. Its daemons are supervised either by
this package (native systemd units, the default on systemd hosts) or by
koha-sysv (the traditional /etc/init.d/koha-common script). Upgrading a
systemd host from the monolithic koha-common installs koha-systemd,
stops the init-script-started daemons, enables a unit for every daemon
of every enabled instance and starts them; no manual action is needed.
What changes for administrators on a koha-systemd host:
* Each daemon is its own unit (koha-plack@INSTANCE.service,
koha-zebra@INSTANCE.service, koha-worker@INSTANCE.service, ...),
grouped by koha@INSTANCE.target and koha.target.
"systemctl restart koha-common" still restarts everything, and the
koha-plack/koha-zebra/koha-worker/... helpers work as before.
* Daemon stdout/stderr goes to the journal, not to
/var/log/koha/INSTANCE/NAME-output.log and NAME-error.log. Use
"journalctl -u koha-zebra@INSTANCE" (or -t INSTANCE-koha-zebra).
Logs written by Koha itself (plack.log, plack-error.log, the log4perl
files such as plack-opac-error.log, sip-output.log, z3950.log) are
unchanged.
* The units are sandboxed (PrivateTmp, ProtectSystem=full, ProtectHome,
NoNewPrivileges). Instances whose koha-conf.xml has an empty
<tmp_path> should set it to /var/lib/koha/INSTANCE/tmp; see
/usr/share/doc/koha-systemd/README.Debian for details and how to
relax a setting with a drop-in.
* SIP is enabled per instance with "koha-sip --enable"; the
/var/lib/koha/INSTANCE/sip.enabled flag file is no longer read on
systemd hosts.
-- Koha Development Team <koha-devel@lists.koha-community.org> Tue, 15 Sep 2026 12:00:00 +0000

View File

@ -1,88 +1,159 @@
# Koha systemd Units
# Koha systemd units
This package provides native systemd service units for Koha ILS.
This package supervises every Koha daemon with native systemd units. It is
one of the two providers of the `koha-init` virtual package that koha-common
depends on; the other is koha-sysv (the traditional /etc/init.d/koha-common
script). The two conflict with each other and installing one replaces the
other. koha-common prefers koha-systemd wherever systemd is installable, so
containers that want the init script must install koha-sysv explicitly.
## Architecture
## Units
Each Koha service type runs as a separate systemd unit using instance templates:
Per instance (`INSTANCE` is the koha-create name, e.g. `library`):
- `koha-plack@.service` - Plack/Starman web application server
- `koha-zebra@.service` - Zebra search indexing server
- `koha-sip@.service` - SIP2 circulation protocol server
- `koha-z3950@.service` - Z39.50/SRU server
- `koha-worker@.service` - Background job worker (default queue)
- `koha-worker-long@.service` - Background job worker (long_tasks queue)
- `koha-indexer@.service` - Zebra indexing daemon
- `koha-es-indexer@.service` - Elasticsearch indexing daemon
- `koha-plack@INSTANCE.service` Plack/Starman web application
- `koha-zebra@INSTANCE.service` Zebra search server
- `koha-indexer@INSTANCE.service` Zebra indexing daemon
- `koha-es-indexer@INSTANCE.service` Elasticsearch indexing daemon
- `koha-worker@INSTANCE.service` background jobs, `default` queue
- `koha-worker-long_tasks@INSTANCE.service` background jobs, `long_tasks` queue
- `koha-sip@INSTANCE.service` SIP2 server
- `koha-z3950@INSTANCE.service` Z39.50/SRU server
- `koha-create-dirs@INSTANCE.service` oneshot that creates the run,
lock and cache directories the
others depend on
- `koha@INSTANCE.target` groups the instance's units
Instance grouping:
- `koha@.target` - Groups all services for a single instance
- `koha.target` - Groups all Koha instances
Host wide:
## Usage
- `koha.target` groups every `koha@INSTANCE.target`; enabled at boot
- `koha-common.service` compatibility shim so that
`systemctl start|stop|restart koha-common` (the old
`service koha-common ...`) keeps working. It has no
state of its own: `systemctl status koha.target`, or
`koha-systemd-ctl status INSTANCE`, is the real
health check.
### Managing a complete instance
## Day-to-day use
```bash
# Start all services for instance "library"
systemctl start koha@library.target
The koha-* helpers work as before and drive systemd underneath:
# Enable at boot
systemctl enable koha@library.target
koha-plack --restart library # systemctl restart koha-plack@library
koha-zebra --stop library
koha-worker --all-queues --status library
# Check status
systemctl status koha@library.target
```
Each helper also has `--enable` and `--disable`, which control whether the
unit is started at boot (with koha-sysv these are no-ops). Or use systemctl
directly:
### Managing individual services
systemctl restart koha@library.target # one instance, all daemons
systemctl restart koha-common # every instance
systemctl status koha-plack@library.service
koha-systemd-ctl status library # all units of one instance
```bash
# Start only Plack for instance "library"
systemctl start koha-plack@library.service
koha-create enables and starts the units of a new instance. koha-disable
stops an instance and disables its `koha@INSTANCE.target`, koha-enable
turns it back on and starts it, and koha-remove disables everything it
created.
# Restart SIP server
systemctl restart koha-sip@library.service
Which units are enabled follows the instance's configuration:
# Check worker status
systemctl status koha-worker@library.service
systemctl status koha-worker-long@library.service
```
- Plack: `koha-plack --enable INSTANCE` (also edits the Apache vhost)
- SIP: `koha-sip --enable INSTANCE`. The SysV-era flag file
/var/lib/koha/INSTANCE/sip.enabled is not consulted on systemd hosts;
`systemctl is-enabled koha-sip@INSTANCE` is the source of truth.
- Z39.50/SRU: `koha-z3950-responder --enable INSTANCE`
- Zebra indexer: enabled when USE_INDEXER_DAEMON=yes in
/etc/default/koha-common (the default)
- Elasticsearch indexer: `koha-es-indexer --enable INSTANCE`. Switching the
SearchEngine system preference does not enable anything by itself;
`systemctl restart koha-common` (or `koha-systemd-ctl sync-es-indexers`)
discovers instances that now use Elasticsearch and enables their indexer,
like the init script did on every start.
### Logs
## Configuration
```bash
# View logs for all services of an instance
journalctl -u 'koha-*@library.service'
The units read the same settings as the init script:
# View Plack logs
journalctl -u koha-plack@library.service -f
- /etc/koha/sites/INSTANCE/koha-conf.xml: plack_workers,
plack_max_requests, debug_mode, zebra_loglevels, zebra_max_record_size,
es_indexer_batch_size, and z3950_responder_options in the Z39.50
config.xml
- /etc/default/koha-common: INDEXER_TIMEOUT, ALTERNATE_INDEXER_DAEMON,
INDEXER_PARAMS, Z3950_ADDITIONAL_OPTS
# View all Koha logs
journalctl -u 'koha-*.service'
```
They are re-read on every start by koha-generate-env, which writes them to
/run/koha/INSTANCE/SERVICE.env for the unit's EnvironmentFile=. Restart the
unit after changing them.
## Service Enablement
## Logging
Some services are conditionally enabled based on configuration files:
This is the one visible difference from the init script. With koha-sysv the
daemon(1) wrapper captured each daemon's standard output and error to
/var/log/koha/INSTANCE/NAME-output.log and NAME-error.log. With systemd
that output goes to the journal instead:
- **SIP**: Requires `/var/lib/koha/{instance}/sip.enabled` and `/etc/koha/sites/{instance}/SIPconfig.xml`
- **Z3950**: Requires `/etc/koha/sites/{instance}/z3950/config.xml`
journalctl -u koha-zebra@library.service
journalctl -t library-koha-worker # by SyslogIdentifier
journalctl -u 'koha-*@library.service' # everything for one instance
## Migration from init scripts
So on a koha-systemd host these files are no longer written:
The koha-systemd package is designed to coexist with koha-common but provides
an alternative service management approach. To migrate:
zebra-output.log, zebra-error.log
indexer-output.log, indexer-error.log
es-indexer-output.log, es-indexer-error.log
worker-output.log, worker-error.log (the worker's log4perl appender
writes to stderr, so its messages
are in the journal too)
sip-error.log (SIP's own log4perl output still
goes to sip-output.log)
1. Stop services via init script: `service koha-common stop`
2. Install koha-systemd package
3. Enable instance target: `systemctl enable koha@{instance}.target`
4. Start services: `systemctl start koha@{instance}.target`
Everything the application writes itself is unchanged, because it is
configured in /etc/koha/sites/INSTANCE/log4perl.conf or passed on the
command line, not captured from stdout:
## Advantages
plack.log, plack-error.log (Starman --access-log/--error-log)
plack-opac-error.log, plack-intranet-error.log, plack-api-error.log,
api-error.log, sip-output.log, z3950-error.log, editrace.log
(log4perl)
z3950.log (z3950_responder.pl -l)
opac-error.log, intranet-error.log, opac-access.log, ...
(Apache, per vhost)
- **Granular control**: Start/stop individual services per instance
- **Resource limits**: Apply CPU/memory limits via systemd
- **Dependency management**: Proper service ordering and failure handling
- **Logging**: Centralized logging via journald
- **Security**: Modern sandboxing options available
- **Monitoring**: Native systemd health checks and status reporting
Existing NAME-output.log/NAME-error.log files are left in place; logrotate
keeps rotating whatever is there.
## Sandboxing
The daemon units run with ProtectSystem=full, ProtectHome=yes,
PrivateTmp=yes and NoNewPrivileges=yes. This is fine for instances created
by koha-create, which keep everything under /var/lib/koha/INSTANCE,
/var/cache/koha/INSTANCE and /var/log/koha/INSTANCE. It matters if:
- koha-conf.xml has an empty <tmp_path> (instances created before that
option existed): Koha then falls back to /tmp, which is private to each
unit, so file-based sessions do not survive a Plack restart and are not
shared with CGI, and uploads staged by Plack are invisible to the worker.
Set <tmp_path>/var/lib/koha/INSTANCE/tmp</tmp_path> (and create it) as
koha-create does today.
- --upload-path, --tmp-path or a plugin directory point under /home
(ProtectHome) or anywhere outside the instance directories (ProtectSystem
makes /usr, /boot and /etc read-only).
- a plugin shells out to a setuid helper such as sudo (NoNewPrivileges).
Relax a setting for one unit with a drop-in rather than editing the
packaged file:
systemctl edit koha-plack@library.service
[Service]
PrivateTmp=no
ReadWritePaths=/srv/koha-uploads
## Migration from koha-sysv / the old koha-common
Installing koha-systemd (which happens automatically when upgrading
koha-common from a pre-split release on a systemd host) stops the daemons
the init script started, enables the units matching each enabled
instance's configuration, and starts koha.target. No manual step is
needed. To go back, `apt install koha-sysv`: koha-systemd is removed, its
prerm stops the units, and the init script takes over on the next start.

View File

@ -29,7 +29,7 @@ use Koha::Z3950Responder;
=head1 SYNOPSIS
z3950_responder.pl [-h|--help] [--man] [-a <pdufile>] [-v <loglevel>] [-l <logfile>] [-u <user>]
[--config-dir <config>] [-t <minutes>] [-k <kilobytes>] [-d <daemon>] [-p <pidfile>]
[-c <config>] [-t <minutes>] [-k <kilobytes>] [-d <daemon>] [-p <pidfile>]
[-C certfile] [-zKiDST1] [-m <time-format>] [-w <directory>] [--debug]
[--add-item-status=SUBFIELD] [--prefetch=NUM_RECORDS] [--config-dir=<directory>]
[<listener-addr>... ]

View File

@ -1,3 +1,5 @@
#!/usr/bin/perl
# Copyright 2026 Martin Renvoize
#
# This file is part of Koha.
@ -19,7 +21,6 @@ use Modern::Perl;
use Test::More;
use Cwd qw( abs_path );
use File::Basename qw( basename );
use FindBin qw( $Bin );
my $analyzer = qx(which systemd-analyze 2>/dev/null);
@ -68,15 +69,11 @@ sub ignorable {
# systemd rewrites legacy /var/run paths itself and only warns
return 1 if $line =~ /references a path below legacy directory/;
# Helper scripts the units call are only installed to /usr/sbin by the
# koha-systemd and koha-core packages, so on a dev checkout ignore
# 'not executable' errors for commands this repo ships
if ( $line =~ /Command (\S+) is not executable/ ) {
my $script = basename($1);
return 1
if -e "$Bin/../debian/scripts/$script"
|| -e "$Bin/../debian/systemd/$script";
}
# The commands the units run (starman, zebrasrv, the koha-* helpers,
# /usr/share/koha/bin/...) only exist on a host with the Koha packages
# installed. Whether they are present says nothing about the unit files
# themselves, so treat the whole class as environmental noise
return 1 if $line =~ /Command \S+ is not executable/;
return 0;
}