Scheduled Tasks in Joomla
|
This content was generated with the assistance of AI and should be verified against the official Joomla documentation before being relied on in production. This sub-section’s Joomla bibliography lists the reference material consulted while preparing these pages. |
Scheduled Tasks is Joomla’s built-in replacement for hand-made cron jobs. It is the component com_scheduler, added in
Joomla 4.1, and it lets an administrator run routine work (purging sessions, rotating logs, releasing checked-out
articles, calling a URL) on a timetable from System → Scheduled Tasks. This page was checked on 2026-10-30 against
Joomla 6.2 (PHP 8.3 or later), the Joomla User Guide at https://guide.joomla.org/ and the com_scheduler and
plugins/task sources of the Joomla CMS repository. The running example is the Tallpine Hiking Club site (package
pkg_tallpine), which needs nightly housekeeping and one custom task that syncs trail closures. How to write such a
task is covered in Scheduled task plugins; this page is about using and
operating the scheduler.
What the scheduler is and who uses it
A task is a row in the database that points at a routine offered by a task plugin, plus a timetable, a priority and a few parameters. The site builder creates tasks; the administrator decides what triggers the scheduler; the developer supplies new routines. Joomla ships with several task plugins and with some tasks already created, and third-party extensions can add routines of their own. The tasks run at most when something starts the scheduler, which is the central design point: Joomla has no daemon, so something has to ask it to look for due tasks. There are three ways to do that and the User Guide advises using only one of them at a time.
The three ways to start the scheduler
The choice is made in System → Scheduled Tasks → Options (the com_scheduler configuration). The fields in
config.xml are lazy_scheduler.enabled (default on), lazy_scheduler.interval (default 300), webcron.enabled
(default off), the generated webcron.key, the read-only webcron.base_link and a webcron.reset_key switch, plus a
timeout value (default 300 seconds) and the permissions tab.
Lazy scheduler
The lazy scheduler needs no setup and is on by default. The system plugin System - Schedule Runner
(plg_system_schedulerunner) listens to onBeforeCompileHead on the site and the administrator. For an HTML page it
asks the task model whether any task is due and, only if one is, loads a small script with the configured interval. The
script then calls com_ajax (plugin=RunSchedulerLazy) in the background and the plugin runs the scheduler, ignoring
a client that has already left. The consequences are:
-
nothing runs on a site nobody visits, so a nightly task on a quiet club site may run late or not at all
-
a due task adds a little load to a visitor’s request cycle, and a visitor may notice a slight slowdown
-
the lazy endpoint carries no user check or key, so it can be triggered by anyone; that is acceptable because it only runs tasks that are already due
-
long tasks are limited by the web server’s execution limits
Use it for development and for small sites where timing does not matter.
Web cron
Web cron lets an external service or the hosting panel’s cron call a Joomla URL. Turn on Web Cron in the options;
saving generates a 20-character key (reset it with Reset Access Key), and the options screen then shows the Webcron
Link. The link has the shape described in the User Guide: the com_ajax endpoint with
plugin=RunSchedulerWebcron&group=system&format=json and the key as the hash parameter. An optional id
parameter selects one task. A request with web cron disabled, or with an empty or non-matching key, is answered with
HTTP 403.
# Every minute from an external cron; keep the real key in the cron environment, not in the page source.
* * * * * curl --silent --fail "https://www.tallpine.example/component/ajax/?plugin=RunSchedulerWebcron&group=system&format=json&hash=${TALLPINE_CRON_HASH}" > /dev/null
Anyone who has the link can start tasks, so treat it as a secret and use Reset Access Key if it leaks, then update the cron. With web cron in place switch the lazy scheduler off; leaving both on can still slow visits now and then. Web cron works from any machine, uses the website’s PHP version and file owner, prevents a new task from starting while another is running, and is subject to the web server’s or host’s request time limit, often a minute or two.
System cron with the CLI
The command line is the most robust option when the host allows cron jobs. No option has to be switched on, no URL or key is needed, and there is no web time limit.
# crontab -e, as the user that owns the Joomla files (for example www-data)
* * * * * /usr/bin/php8.3 /var/www/tallpine/cli/joomla.php scheduler:run --all > /dev/null 2>&1
Points that cause real incidents:
-
PHP version. Give the full path to a supported PHP binary (PHP 8.3 or later for Joomla 6). The default
phpin a cron environment is often older than the web server’s. -
File ownership. Run the job as the website user. A task such as Rotate Logs or Check Files that runs as
rootleaves files the web server then cannot overwrite. -
Output. Appending
> /dev/null 2>&1stops cron from mailing every run’s output. -
Overlap. The User Guide notes that the CLI route does not check for a running task, so a job that can outlast the cron interval should be safe to run twice or be scheduled less often. The scheduler itself also takes a per-task lock, described below.
The commands, run from the Joomla root (or with the full path to cli/joomla.php) are:
| Command | Effect |
|---|---|
|
Without options it starts only the next due task, so tasks wait for each other, one per invocation. |
|
Runs every task that is due. |
|
Runs the enabled task with ID 5 immediately, whether it is due or not. |
|
Lists the tasks with ID, state and next run time. |
|
Enables, disables or trashes a task. Check |
More on the console in The Joomla CLI.
Which one to choose
| Lazy scheduler | Web cron | System cron (CLI) | |
|---|---|---|---|
Setup |
None |
Enable in options, add an external cron |
Add a cron job on the server |
Timing |
Only when visitors arrive |
As precise as the cron interval |
As precise as the cron interval |
Time limit |
Web server limit |
Web server or host limit |
None |
Secret |
None |
Key in the URL |
None |
PHP and user |
The site’s |
The site’s |
Whatever the cron job uses |
Best for |
Development, tiny sites |
Shared hosting with a remote cron service |
VPS, containers, long tasks |
The recommendation of the User Guide is: precise timing with a remote cron means web cron plus the lazy scheduler switched off; long tasks on a server you control mean the command line. Older books such as Joomla! 4 Masterclass (2023) introduce the scheduler with the lazy mode only; for a production site that is the weakest choice.
Docker Compose
In a container the cron job belongs in its own service that runs the same image as the site, so PHP and the file layout match.
services:
joomla:
image: joomla:6.2-php8.4-apache
volumes:
- tallpine_site:/var/www/html
scheduler:
image: joomla:6.2-php8.4-apache
volumes:
- tallpine_site:/var/www/html
working_dir: /var/www/html
user: www-data
entrypoint: ["/bin/sh", "-c"]
command:
- "while true; do php cli/joomla.php scheduler:run --all; sleep 60; done"
volumes:
tallpine_site:
The site volume holds configuration.php, so the second service reaches the same database. The surrounding Compose file
(MariaDB, Mailpit) is in Local development with Docker.
Creating and editing a task
System → Scheduled Tasks → New first asks for the task type, which is the list of routines contributed by enabled
task plugins; a plugin that is disabled contributes no type, and a task whose plugin is later disabled becomes
orphaned. The edit form (forms/task.xml) has these parts:
-
Title (required, 100 characters), Note and Status: enabled, disabled or trashed.
-
Execution rule: interval in minutes, hours, days or months, a cron expression, or manual execution only. A days interval takes a time; a months interval takes a day of the month (1 to 31) and a time. The cron expression is five fields (minutes, hours, days of month, months, days of week), useful to spread tasks so they do not all start at once. Manual tasks run only from the yellow Run Task button in the list or the CLI with
--id. -
Priority: Low (-1), Normal (0) or High (1). Due tasks are picked by priority first and then by the oldest next execution.
-
Logging: an individual log switch; when on, the task writes to its own file (by default
task_ID.log.php) in the Joomla log folder, in addition to the general log under the categorytaskplus its ID. -
Read-only history: last and next execution, times executed, times failed and last exit code.
-
Permissions: the task’s ACL; the
core.testrunaction allows a user to run the task from the administrator, and the task’s creator may also test it while holdingcore.create.
Task-specific parameters live on the plugin’s own tab; for example the Delete Action Logs task asks for the retention in days.
Example: Tallpine nightly housekeeping
The club wants these tasks, all created from the administrator interface:
| Task | Rule | Reason |
|---|---|---|
Session Data Purge |
Every 6 hours |
Keep the session table small |
Rotate Logs |
Days: 1, at 02:30 |
Keep |
Delete Action Logs |
Days: 1, at 03:00, retention 180 |
Privacy retention, see Privacy and action logs |
Global Check-in |
Hours: 1, delay 2 hours |
Release articles left open by editors |
Update Notification |
Days: 1, at 07:00 |
Mail Super Users when a core update exists |
Tallpine trail sync |
Minutes: 30 |
Custom routine from |
A cron expression equivalent to "every 6 hours starting at 05:00" is minutes 0, hours 5,11,17,23, any day, any
month, any weekday.
The core task plugins
Joomla 6.2 ships nine task plugins in the task plugin group (plugins/task). Each exposes one routine, except Site
Status, which exposes three. Behaviour below is taken from the plugin sources.
Check Files (checkfiles)
Routine checkfiles.imagesize. It scans a folder (a path relative to the site root, two directory levels deep) for
jpg, jpeg, png, gif and webp images and, when the chosen side (width or height) exceeds the configured limit in
pixels, shrinks the image in place keeping the aspect ratio. A maximum number of images per run keeps each execution
short. The original is overwritten, so run it on a copy of images/ first. A folder that does not exist logs a warning
and does not run; a failed resize ends with a failure status.
Delete Action Logs (deleteactionlogs)
Routine delete.actionlogs. The parameter logDeletePeriod is the number of days to keep; older rows are deleted from
the action-logs table. A value of 0 or less deletes nothing and still reports success. A database error gives a failure
status. This is the task that enforces the retention policy described in
Privacy and action logs.
Global Check-in (globalcheckin)
Routine plg_task_globalcheckin_task_get. It checks in every table of the site (prefix included) that has both
checked_out and checked_out_time columns, which is the same sweep as System → Global Check-in. With a delay in
hours greater than zero only items checked out earlier than that are released; with zero or no value everything is
released, including an article an editor is working on right now. A delay of a few hours is the safe setting.
Privacy Consent (privacyconsent)
Routine privacy.consent. It does two jobs in order. First it invalidates active consents older than the expiry in days
and notifies the Super Users with the affected username. Then it sends a reminder to users whose consent expires within
the reminder window (a mail template, with a link to the privacy component’s remind view and a token whose hash is
stored). If a parameter is left empty its value is read as 0 rather than the documented default, so always fill in both
the expiry and the reminder days. See Privacy and action logs.
Requests (requests)
Routine plg_task_requests_task_get. It sends an HTTP GET to a URL with a timeout, optionally with an Authorization
header built from a type and a key, and saves the response body as task_ID_response.html under the Joomla root
directory. Only status 200 is a success; any other status is a failure, and a transport exception is reported as a
timeout. It supports no other method, headers or body, so it fits "ping this URL" jobs (warming a cache, hitting an
external health check), not API integrations. The stored key sits in the task’s parameters and the target URL is not
validated, so give task-editing permission only to trusted administrators and never point it at an address you do not
control. A custom routine, as in plg_task_tallpine_trailsync, is the better tool for authenticated API work.
Rotate Logs (rotatelogs)
Routine rotation.logs. It renames .php files in the configured log path with a numeric prefix (error.php becomes
1.error.php, 1.error.php becomes 2.error.php), working from the highest number down, and deletes files whose
number reaches the configured number of logs to keep. The parameter must be set: an empty value is read as 0, which
deletes every log file. Set it to the number of generations you want to retain, for example 7 with a daily rule.
Session GC (sessiongc)
Routine session.gc, with two switches: Enable session garbage collection (calls the session handler’s GC) and
Enable session metadata garbage collection (deletes metadata older than the session lifetime). Both default to
disabled when absent from the task’s stored parameters, so open the task and save it with both on.
Site Status (sitestatus)
Three routines take no parameters: plg_task_toggle_offline (toggle), plg_task_toggle_offline_set_online and
plg_task_toggle_offline_set_offline. Each rewrites configuration.php with the new value of the offline flag, adjusts
permissions around the write and clears the OPcache entry. Used as a pair, two tasks give the Tallpine site a nightly
maintenance window: one sets the site offline at 03:30, another online at 03:40. It needs configuration.php to be
writable by the user running the scheduler, which conflicts with a hardened read-only configuration (see
configuration.php and .htaccess); on a hardened site run it from
the CLI as the file owner or do not use it.
Update Notification (updatenotification)
Routine update.notification. If a core update newer than the installed version exists it mails the Super Users, using
the mail template plg_task_updatenotification.mail. Recipients are users with core.admin at the root who are not
blocked and have system emails enabled; the optional Email parameter restricts that to the listed addresses that match
eligible users, falling back to all eligible Super Users if none match. A language override forces the mail language.
It stays quiet for a short time after an update check when automated core updates are subscribed. Edit the text with
mail templates. It only notifies; the update itself is covered in
Updating Joomla and Automated Core Updates.
Locking, timeouts and exit codes
Each task row has a locked timestamp. The scheduler takes the lock when it fetches the task, with a conditional
update that succeeds only when the column is empty or older than the timeout option (300 seconds by default), and
clears it when the routine finishes. This is why two overlapping triggers cannot run the same task at once, and why a
crashed process (killed PHP, server restart) blocks its task only until the timeout has passed. If you have tasks that
legitimately run for more than 300 seconds, raise the timeout or let the routine process work in batches and return the
resume status.
A routine returns an integer status (the Status class):
| Code | Name | Meaning |
|---|---|---|
0 |
OK |
Success. |
2 |
NO_LOCK |
The lock could not be acquired. |
3 |
NO_RUN |
The task could not run, for example a missing precondition. |
4 |
NO_RELEASE |
The lock could not be released or the record could not be updated. |
5 |
KNOCKOUT |
An exception stopped the routine. |
123 |
WILL_RESUME |
More batches remain; the task is rescheduled almost at once and counts as one execution after the last batch. |
124 |
TIMEOUT |
The routine timed out. |
125 |
NO_TASK |
The task does not exist. |
127 |
NO_ROUTINE |
The routine is missing, for instance because its plugin is disabled. |
Anything other than OK or WILL_RESUME increases the task’s failure counter. Returning nothing yields -1 and a non-integer yields -2.
Monitoring
-
The task list shows state, last run, next run and last exit code. The Last Run Date column tells you quickly whether the trigger works at all; a task that never runs after the lazy scheduler was switched off usually means the cron job was never added or runs with the wrong PHP.
-
php cli/joomla.php scheduler:listshows the same from a shell, so it can be used in a health check. -
Turn on the individual log of a critical task to get its own file, and read the general logs under the category
task. -
The scheduler component’s changes are written to the action log (component
scheduler); see Privacy and action logs. -
Test a task by hand with its Run Task button, or with
scheduler:run --id=Nas the web server user, before relying on its schedule.
# Health check for a monitoring system: fail when no task is listed for the Tallpine site
docker compose exec -T -u www-data joomla php cli/joomla.php scheduler:list
docker compose exec -T -u www-data joomla php cli/joomla.php scheduler:run --id=5
Common errors and pitfalls
-
Two triggers at once. Lazy and web cron or CLI together still work but waste work and slow visits; pick one.
-
Wrong PHP binary in cron. The cron job fails on syntax or missing extensions while the site works; use an explicit
/usr/bin/php8.3. -
Wrong user. Logs, rotated files or
configuration.phpend up owned byroot. -
Empty numeric parameters. Rotate Logs, Privacy Consent, Session GC and Global Check-in treat an unset value as 0, with effects ranging from nothing happening to deleting all logs. Always open and save every core task once and check its parameters.
-
Disabled plugin. A task whose plugin is disabled has no routine and fails with the missing-routine status.
-
Exposed key. A web cron URL in a shared ticket or a screenshot lets anyone run tasks; reset the key.
-
Behind a reverse proxy. A web cron request that is redirected (HTTP to HTTPS, missing
www) can lose its query string or be answered by a cache; call the final HTTPS address directly and exclude the path from page caching.
Version notes
Scheduled Tasks arrived in Joomla 4.1 as com_scheduler, together with the task plugin group; the nine core task
plugins above are present in 6.2. For authors of custom routines the 5.0 concrete event classes and the removal in 7.0
of the automatic $app and $db plugin properties matter (see
Scheduled task plugins). The source tree of com_scheduler is the
reference for internals and the User Guide is the reference for operation. Joomla 4 is end of life and 5.x is
security-only; new sites should start on 6.2 (see
Versions, release cycle and support).