Schedule Nextcloud's cron.php in the user's crontab, not a systemd timer
The timer was justified partly on XDG_RUNTIME_DIR: a user crontab "would need it faked into every line". That is wrong here. The caddy playbook enables lingering, so /run/user/<uid> exists whether or not anyone is logged in, and that is where podman keeps the state `exec` reads. Nothing needs to be set. The one real constraint is that the job cannot go in root's crontab: root cannot `podman exec` into another user's rootless containers. This user's crontab is fine, and the play runs as that user, so `ansible.builtin.cron` with no `user:` writes exactly there. That replaces four tasks and two template files with one task and no files, and it is what the service had before the Quadlet conversion. The cost is that cron sends its output to local mail instead of the journal; the readme says so and names the timer as the alternative. While here, the XDG_RUNTIME_DIR asides come out of the comments that carried them: the play runs over SSH, so `systemctl --user` works because systemd's user manager is reachable over that connection, and that is all a reader needs to know.
This commit is contained in:
1 parent
05452127ec
commit
5d63f3f0e0
9 files changed
+46
-118
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
# Caddy is its own playbook (caddy/playbook.yml) and a rootless Quadlet unit
|
||||
# there. These handlers run as the connection user, so `systemctl --user` and
|
||||
# `podman exec` work without any XDG_RUNTIME_DIR juggling.
|
||||
# there. These handlers run as the connection user, which is what makes
|
||||
# `systemctl --user` and `podman exec` work here.
|
||||
|
||||
# Notified by this role when the Caddyfile changes. A reload keeps existing
|
||||
# connections; a restart would briefly drop every hosted site.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
# All handlers run as the connection user ({{ podman_user }}), so systemctl
|
||||
# --user works without setting XDG_RUNTIME_DIR.
|
||||
# Handlers run as the connection user, which is what makes `systemctl --user`
|
||||
# work here.
|
||||
|
||||
- name: Reload systemd user units
|
||||
ansible.builtin.command: systemctl --user daemon-reload
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
# Nextcloud as a rootless Podman Quadlet unit.
|
||||
#
|
||||
# Everything runs as the connection user: the units belong to it, so
|
||||
# `systemctl --user` works through the SSH session's own PAM session — no become,
|
||||
# no XDG_RUNTIME_DIR juggling. Requires gather_facts (set in playbook.yml).
|
||||
# Everything runs as the connection user; requires gather_facts (set in
|
||||
# playbook.yml), which is where podman_user and the home directory come from.
|
||||
#
|
||||
# A fresh webroot is a valid state. The image initialises the code and serves the
|
||||
# setup wizard, and this play succeeds. Set nextcloud_admin_user and
|
||||
@@ -110,40 +109,15 @@
|
||||
denied" under /usr/local/etc/php/conf.d, anything about `db` or `redis`,
|
||||
and "downgrading is not supported" each point somewhere different.
|
||||
|
||||
# Nextcloud wants `cron.php` every five minutes, and it cannot be a crontab entry:
|
||||
# root cannot `podman exec` into another user's rootless containers, and a
|
||||
# container has no crontab of its own. A systemd user timer is the equivalent, and
|
||||
# it comes back after a reboot with the rest of the stack.
|
||||
- name: Ensure the systemd user unit directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ user_unit_dir }}"
|
||||
state: directory
|
||||
owner: "{{ podman_user }}"
|
||||
group: "{{ podman_user }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Install the background-job service
|
||||
ansible.builtin.template:
|
||||
src: nextcloud-cron.service.j2
|
||||
dest: "{{ user_unit_dir }}/{{ cron_unit }}.service"
|
||||
owner: "{{ podman_user }}"
|
||||
group: "{{ podman_user }}"
|
||||
mode: '0644'
|
||||
|
||||
- name: Install the background-job timer
|
||||
ansible.builtin.template:
|
||||
src: nextcloud-cron.timer.j2
|
||||
dest: "{{ user_unit_dir }}/{{ cron_unit }}.timer"
|
||||
owner: "{{ podman_user }}"
|
||||
group: "{{ podman_user }}"
|
||||
mode: '0644'
|
||||
|
||||
# A plain .timer is not generated, so unlike the Quadlet units it does have to be
|
||||
# enabled — that is what brings it back after a reboot.
|
||||
- name: Enable and start the background-job timer
|
||||
ansible.builtin.systemd_service:
|
||||
name: "{{ cron_unit }}.timer"
|
||||
scope: user
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
# Nextcloud wants `cron.php` every five minutes. This has to be *this user's*
|
||||
# crontab, not root's: root cannot `podman exec` into another user's rootless
|
||||
# containers. It works without any environment plumbing because the caddy
|
||||
# playbook enables lingering, so /run/user/<uid> exists whether or not anyone is
|
||||
# logged in — which is where podman keeps the state `exec` needs.
|
||||
- name: Schedule Nextcloud's background jobs
|
||||
ansible.builtin.cron:
|
||||
name: Nextcloud background jobs
|
||||
minute: "{{ cron_minute }}"
|
||||
job: >-
|
||||
/usr/bin/podman exec -u www-data {{ container_name }}
|
||||
php {{ webroot }}/cron.php
|
||||
@@ -1,26 +0,0 @@
|
||||
# Nextcloud background jobs — systemd user service.
|
||||
# Managed by Ansible (nextcloud/roles/nextcloud). Do not edit by hand.
|
||||
#
|
||||
# Nextcloud expects `cron.php` every five minutes. It cannot be a crontab entry:
|
||||
# root cannot `podman exec` into another user's rootless containers, and there is
|
||||
# no container user to own a crontab. A user service started by the timer next to
|
||||
# it is the equivalent, and it is what comes back after a reboot with the rest of
|
||||
# the stack.
|
||||
#
|
||||
# No XDG_RUNTIME_DIR juggling is needed: systemd's *user* manager sets it for
|
||||
# every unit it starts, which is also why the whole playbook runs as the
|
||||
# connection user rather than through become.
|
||||
|
||||
[Unit]
|
||||
Description=Nextcloud background jobs (cron.php)
|
||||
After={{ unit_name }}
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
# `-u www-data` matters: cron.php must run as the user that owns config.php, and
|
||||
# under keep-id that is uid {{ app_uid }}.
|
||||
ExecStart=/usr/bin/podman exec -u www-data {{ container_name }} php {{ webroot }}/cron.php
|
||||
# A long-running job must not pile up behind itself; the next timer tick would
|
||||
# start a second copy. Generous, because a first run after an upgrade can have a
|
||||
# lot of queued work.
|
||||
TimeoutStartSec=900
|
||||
@@ -1,19 +0,0 @@
|
||||
# Nextcloud background jobs — systemd user timer.
|
||||
# Managed by Ansible (nextcloud/roles/nextcloud). Do not edit by hand.
|
||||
#
|
||||
# Fires nextcloud-cron.service every five minutes, which is the interval
|
||||
# Nextcloud's own installation guide asks for.
|
||||
|
||||
[Unit]
|
||||
Description=Run Nextcloud background jobs every five minutes
|
||||
|
||||
[Timer]
|
||||
OnCalendar={{ cron_schedule }}
|
||||
# Catch up once if a slot was missed while the host was down, so a reboot does
|
||||
# not delay the first run by five minutes. It is not a queue: one catch-up run,
|
||||
# never one per missed slot.
|
||||
Persistent=true
|
||||
Unit={{ cron_unit }}.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
# Postgres is its own playbook (postgres/playbook.yml) and a rootless Quadlet
|
||||
# unit there. This handler runs as the connection user, so `systemctl --user`
|
||||
# works without setting XDG_RUNTIME_DIR.
|
||||
# unit there. This handler runs as the connection user, which is what makes
|
||||
# `systemctl --user` work here.
|
||||
#
|
||||
# Required by postgres/roles/attach, which notifies it whenever this app's
|
||||
# drop-in changes: a new drop-in changes the generated unit, so Postgres must be
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
# Valkey is its own playbook (valkey/playbook.yml) and a rootless Quadlet unit
|
||||
# there. This handler runs as the connection user, so `systemctl --user` works
|
||||
# without setting XDG_RUNTIME_DIR.
|
||||
# there. This handler runs as the connection user, which is what makes
|
||||
# `systemctl --user` work here.
|
||||
#
|
||||
# Required by valkey/roles/attach, which notifies it whenever this app's drop-in
|
||||
# changes: a new drop-in changes the generated unit, so Valkey must be recreated
|
||||
|
||||
Reference in new issue
Block a user