apnscp version: 3.2.0 (build 46e264822)
OS: AlmaLinux 9.8
Reproducible: yes — steps below
What happens
logrotate.service is killed by SIGUSR1 and dies mid-run:
logrotate.service: Main process exited, code=killed, status=10/USR1
Everything that sorts after php-fpm in /etc/logrotate.d silently stops rotating for that run (on a stock box that’s the rsyslog set — messages, secure, maillog, cron), and logrotate.status isn’t written, so that run’s completed rotations repeat next time. It’s a signal kill rather than a PHP exception, so debug mode / a backtrace doesn’t apply here.
Why
The generated /etc/logrotate.d/php-fpm (from misc-logrotate/templates/php-fpm.j2, line 7) is:
/bin/kill -SIGUSR1 "$(systemctl show -pMainPID php-fpm-{{ fpm_service_name }} | cut -d= -f2)" 2> /dev/null || true
php-fpm pools are socket-activated, so an idle pool is inactive (dead) and its MainPID is 0:
# systemctl show -pMainPID php-fpm-MAIN
MainPID=0
cut -d= -f2 gives 0, so the line runs kill -SIGUSR1 0. Per POSIX, kill with PID 0 signals every process in the sender’s own process group — and logrotate runs postrotate scripts in its own process group (no setsid/setpgid), so the signal lands on logrotate itself. logrotate installs no USR1 handler, so it terminates. 2> /dev/null || true doesn’t save it: the kill succeeds, it just hits the wrong target.
Steps to reproduce
With the pool idle (systemctl show -pMainPID php-fpm-MAIN → MainPID=0), force the stanza:
logrotate -f /etc/logrotate.d/php-fpm
→ the logrotate process is killed by SIGUSR1.
In normal daily runs it needs two things at once: the pool’s log non-empty (so notifempty actually rotates it) and the pool not running — e.g. after a reboot that leaves the pool idle. It also self-arms: a successful USR1 makes php-fpm log error log file re-opened, so the log is non-empty the next night.
Fix
Use the systemd-native form apnscp already ships in /etc/logrotate.d/rspamd — it targets the unit’s own main process and is a safe no-op when the pool is down:
systemctl --signal=USR1 --kill-who=main kill php-fpm-{{ fpm_service_name }}.service 2>/dev/null || :
(--kill-who is --kill-whom on systemd ≥ 252; both work.) That’s also the semantically correct behaviour: a dead pool holds no open handle on the rotated file, and re-opens the new path when socket activation next starts it.
Note for existing installs
The task deploys with force: "{{ force | default(False) }}", so upcp won’t re-render an existing /etc/logrotate.d/php-fpm — a template fix alone won’t reach current boxes; they’d need a migration.
Environment: apnscp 3.2.0 (46e264822), AlmaLinux 9.8, logrotate 3.18.0, systemd 252, util-linux 2.37.4.