Moodle Scheduled Tasks: Single CLI Cron Architecture Note
Keep Moodle background work on a single CLI cron entrypoint run as the web service user, with observable task logs and clear trust and data boundaries. Know when to move to a queue or worker split.
26 Oct 2025, 23:23 UTC

Problem: background work fails silently
Moodle relies on scheduled background work for queued email, calendar updates, adhoc tasks and plugin maintenance. When that work stops, features degrade gradually with no immediate error page. The useful takeaway is to keep one centralized CLI entrypoint, run it as the web service user, and observe last-run times and task logs rather than adding wrappers or HTTP triggers.
Requirements for reliable background work
Core requirements are timely execution, single ownership of work, observability, and safe file access.
- Asynchronous core work must run on a predictable interval, typically every few minutes, without overlapping runs.
- Plugin tasks should run only when the plugin is installed and enabled; adhoc tasks handle one-off work.
- Execution must be observable via last-run timestamps and task log entries.
- File writes to moodledata must be consistent between web requests and CLI.
Do not build a parallel scheduler or wrapper daemon. Moodle provides task retry and backoff settings that should be used instead.
Smallest suitable design
The smallest suitable design is one host scheduler invocation of the core CLI entrypoint.
Run from the host crontab as the service user that serves the web application, for example:
*/5 * * * * www-data php /var/www/moodle/admin/cli/cron.php >> /var/log/moodle/cron.log 2>&1Where to run: on the host that can reach the Moodle code and moodledata, with the PHP binary used by the web stack. Required permissions: read access to the Moodle code directory, write access to moodledata, and write access to the log path. Risks: running as root creates file ownership drift; running as a different user than the web server causes permission errors on uploads and caches.
Avoid feature-specific scripts. Keep core scheduled tasks enabled and add plugin tasks only when the plugin is installed. Use adhoc tasks for one-off work. Verify the setting for CLI-only cron in the installed version before relying on it, as admin paths and defaults are release-sensitive.
Trust boundary: OS user and web separation
Cron runs PHP as the OS user chosen by the scheduler. That user needs write access to moodledata and should match the web server user for file consistency. It must not be root or an interactive admin account.
Web requests remain untrusted. Task administration is capability-gated in the UI. If HTTP-triggered cron is used, protect the endpoint and prefer supported CLI-only configuration. Confirm configuration on the deployed version.
Data boundary and safe task behavior
Tasks should read and write only through Moodle APIs and approved storage. Keep credentials outside code, typically via config.php or environment variables managed by the platform.
Treat external calls as fallible with timeouts. Avoid writing outside moodledata and avoid passing raw user input to shell commands. New plugin scheduled tasks can add long-running or unsafe work; review them in staging before enabling in production.
Operational checks
Confirm the scheduler actually fires.
- Review last-run times on the scheduled tasks screen in Site administration > Server > Scheduled tasks.
- Check recent task-log entries for completions and errors.
Alert when cron has not run within the expected interval, when tasks fail repeatedly, or when run duration grows unexpectedly. In staging, pause the scheduler, run admin/cli/cron.php manually, and confirm the scheduled tasks screen and task log show the expected run and completions.
Check process and file ownership to ensure cron runs as the intended service user, matches the web server for moodledata writes, and never runs as root. Using a non-production clone, induce a task failure and confirm the task log records it, retries follow the configured backoff, and other tasks still run.
Failure modes
Missing cron degrades features gradually rather than visibly, e.g., delayed emails and stale reports.
Duplicate cron entries or weak locking can cause contention or repeated work. Blocking external calls can starve the queue.
Permission drift between web and CLI users causes partial or unreadable files in moodledata.
Conditions that change the design
High volume or multiple web nodes justify a dedicated queue/worker or split cron classes to avoid lock contention.
Inbound identity, enrolment or webhook integrations need authenticated idempotent endpoints separate from the generic cron.
Containers, read-only web nodes or managed databases can invalidate the single-cron assumption. In those cases, re-evaluate where moodledata lives and whether a single node can safely own scheduled work.
Limitations: exact admin paths, locking behavior and CLI-only options are release-sensitive. Confirm them against the installed Moodle version before relying on them. Practical verification is to trigger a test asynchronous side effect such as a subscribed forum email and verify delivery timing matches the configured schedule after cron runs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.