Transform Learning with a Powerful Enterprise LMS

Learn more
Moodle

Moodle Cron Not Running: Diagnosis and Fixes for Moodle 4.5 to 5.3

Why Moodle cron stops, how to diagnose it from the status check and a manual run of admin/cli/cron.php, the fix for each cause, and how to confirm and prevent it.

Learn more about this topic with AI

Gemini opens with the prompt copied - paste it into the chat to start.

Step path illustration for diagnosing a Moodle cron that is not running, from the status check to fixing the crontab and stuck tasks

A Moodle cron not running almost always comes down to one of four things: there is no system cron entry, the entry points at the wrong path or PHP binary, the script starts but exits straight away (cron disabled, an upgrade pending or CLI maintenance mode), or cron runs but its task runners are blocked. The fastest diagnosis is to run php admin/cli/cron.php by hand as the web server user and read what it prints. On Moodle 5.1 and later that script lives in the project root, not in public/, which is the most common reason a working crontab breaks after an upgrade.

This guide follows the order we use on client sites: what the problem looks like, the likely causes ranked, a diagnostic procedure, the fix for each cause, how to confirm the fix and how to stop it happening again. Checked against the Moodle 5.3.0 source and the Task API developer documentation (October 2026); script names and settings are the same on 5.1 and 5.2, and the relative path is the same on 4.5 LTS, where the code root is also the web root.

The problem: what Moodle cron does

Moodle does almost all of its background work through the Task API. Scheduled tasks run on a timetable, such as sending forum notification emails, automated course backups and cleanup jobs. Ad hoc tasks are queued by code to run once, as soon as possible or at a set time, such as large course deletions and resets, or the question bank migration that runs after an upgrade to 5.0 or later.

None of these run on their own. As the developer documentation puts it, tasks only run as often as cron runs, and cron should run at least once per minute. "Cron" here means the operating system scheduler starting admin/cli/cron.php, which then picks up whatever scheduled and ad hoc tasks are due. If that script never starts, or starts and exits, the site keeps serving pages while its background work quietly stops.

Symptoms of a stopped cron

Moodle reports the problem itself, in two places:

  • The admin notifications page (Site administration home) shows that the admin/cli/cron.php script "has not been run for" a given time and should run every minute, or that it "has never been run". On sites where web cron is allowed, the same message names admin/cron.php instead.
  • Site administration > Reports > System status shows the "Cron running" check. It turns to a warning when the last run is more than a minute overdue and to critical after a day.

If cron runs but too rarely, you see a different warning: there was a given time "between the last two runs of the cron maintenance script" and it should run every minute. That points at the schedule, not at a broken script.

Users notice before admins do. Typical reports are forum and notification emails that never arrive, course deletions or resets that stay pending, automated backups missing from the backup report, and completion or badge changes that do not appear. On Site administration > Server > Tasks > Scheduled tasks, the "Last run" column stops moving and many "Next run" values show ASAP.

Root causes, ranked by likelihood

This ranking reflects what we find most often on sites we take over, not a survey.

RankCauseTypical sign
1No system cron entry at all"has never been run", often after a new install or a server move
2Wrong script path, often after the 5.1 move to public/Cron stopped on the day of an upgrade or migration; PHP reports "Could not open input file"
3Script exits at once: cron disabled, upgrade pending or CLI maintenance modeLast run stops moving; a one-line message when run by hand
4Wrong user or wrong PHP binaryPermission errors in moodledata, missing extension errors, or a different PHP version than the web server
5Web cron blockedA URL-based scheduler gets "Sorry, internet access to this page has been disabled by the administrator."
6Cron runs but tasks do not"Concurrency limit reached", stuck entries under Tasks running now, or a growing fail delay

Diagnostic procedure

Work through these steps in order. Each one rules out a cause from the table.

  1. Read the status check. Open Site administration > Reports > System status, or from the project root run sudo -u www-data php admin/cli/checks.php -v --filter=cron. Note the time since the last run.
  2. Find the cron entry. List the crontab of the user that should run cron, for example sudo crontab -u www-data -l, and also check /etc/cron.d/ and any container or orchestration scheduler you use. No entry means cause 1.
  3. Check the path in that entry. On 5.1 and later the script is <project root>/admin/cli/cron.php. There is no public/admin/cli directory, so an entry pointing there fails. Confirm the file exists at the exact path in the crontab.
  4. Run it by hand, once. From the project root run sudo -u www-data /usr/bin/php admin/cli/cron.php --keep-alive=0, using the same PHP binary as the crontab. --keep-alive=0 makes it run one pass and exit instead of polling for the default three minutes.
  5. Read the first lines of output. A healthy run prints the server time, the tasks it runs and "Cron run completed correctly". The messages in the next section each map to a cause.
  6. Look at what is running. php admin/cli/cron.php --list prints the PID, host, type, running time and class of every running task. Compare it with Site administration > Server > Tasks > Tasks running now.
  7. Check individual tasks. On the Scheduled tasks page, look for tasks that are disabled or have a non-zero fail delay, then open the task logs (Site administration > Server > Tasks > Task logs) for the failing ones.

Several cron.php processes in ps is not a fault on its own. Since Moodle 4.2 each process keeps polling for new tasks for the keep alive time (default three minutes), so with a one-minute schedule a few overlap by design.

Fixes for each cause

1. No cron entry

Add one for the web server user (www-data on Debian and Ubuntu; apache or nginx on other distributions) with sudo crontab -u www-data -e:

# Moodle cron, every minute. Adjust the PHP and project root paths.
* * * * * /usr/bin/php /var/www/moodle/admin/cli/cron.php >/dev/null 2>&1

This example is not run on a live site; test on staging first. Use the full path to PHP, because cron runs with a minimal environment. While you are debugging, send the output to a log file instead of /dev/null, and switch back (or add log rotation) once it works.

2. Wrong path after an upgrade or move

Since Moodle 5.1 the web server serves only public/, while config.php, Composer files and admin/cli/ stay in the project root. If you repointed the web server and also edited the crontab to point into public/, point it back at <project root>/admin/cli/cron.php. After a server migration, also check that the crontab moved with the site and names the new directory. Our Moodle hosting requirements article covers the 5.1 directory layout in full.

3. The script exits straight away

Message when run by handFix
"Cron is disabled. Use --force to override."Re-enable it: php admin/cli/cron.php --enable, or tick Enable cron under Site administration > Server > Tasks > Task processing.
"Moodle upgrade pending, cron execution suspended."Finish the upgrade: php admin/cli/upgrade.php (add --non-interactive in scripts), or complete it from the notifications page.
"CLI maintenance mode active, cron execution suspended."If the maintenance window is over, run php admin/cli/maintenance.php --disable.

A disabled cron is easy to forget: it is often switched off before a server restart or a deployment, with --disable or --disable-wait, and never switched back on. Add the matching --enable to the same runbook or deployment script.

4. Wrong user or PHP binary

Run cron as the web server user, never as root. Files that a root-owned cron writes into moodledata, such as caches and temporary files, can then not be written by the web server. If that has happened, fix ownership on moodledata before starting cron again.

The PHP CLI is a separate binary with its own php.ini. If /usr/bin/php is a different version from your PHP-FPM pool, or lacks extensions Moodle needs, cron fails while the website works. Use the versioned binary that matches the web server (for example /usr/bin/php8.3 on Debian-based systems) and give the CLI the same extensions and limits. Set the same path in Site administration > Server > System paths > Path to PHP CLI, which the "Run now" button on the Scheduled tasks page needs.

5. Web cron blocked

"Cron execution via command line only" (cronclionly, under Site administration > Security > Site security settings) is on by default, because running cron from a browser can expose privileged information. A scheduler that calls admin/cron.php over HTTP then gets "Sorry, internet access to this page has been disabled by the administrator." Move to the CLI script. If you have no shell access at all, the fallback is to turn cronclionly off and set "Cron password for remote access" (cronremotepassword). Web cron also runs with keep alive disabled.

6. Cron runs, tasks do not

  • "Skipping processing of scheduled tasks. Concurrency limit reached." All scheduled task runner slots are taken. The limit is set under Task processing (default 3 for both scheduled and ad hoc tasks; 0 disables processing). Check Tasks running now for a task that has been running for hours, and fix that task rather than raising the limit. The runner lifetime settings (default 30 minutes each) set how old a runner can get before its slot is freed.
  • Failing tasks. A task that throws an exception is retried after a minute, then with longer gaps up to 24 hours, shown as the fail delay. Run it alone with debugging: php admin/cli/scheduled_task.php --execute='\core\task\session_cleanup_task' --showdebugging, replacing the class with the failing one. Fix the cause, then clear the fail delay on the Scheduled tasks page.
  • Stuck ad hoc tasks. Use php admin/cli/adhoc_task.php --failed to rerun only failed tasks, or --id=N for one task, with --showdebugging for detail. By default an ad hoc task gets 12 retries.
  • Disabled tasks. A task disabled on the Scheduled tasks page, or with scheduled_task.php --disable, never runs. Re-enable it with --enable='\class\name' or reset its schedule to the defaults.
  • Locks on clusters. Every node that runs cron must use a lock factory that works across nodes. File locking in moodledata only works where the shared file system supports locking; database locking ($CFG->lock_factory in config.php) avoids that dependency.

Slow tasks usually trace back to the database or storage rather than cron. Our Moodle performance optimisation guide covers how we profile them.

Confirm the fix

  1. Wait two or three minutes, then reload Site administration > Reports > System status. "Cron running" should read "Cron is running frequently", with a time since the last run under a minute or two.
  2. On the Scheduled tasks page, check that "Last run" values are recent and that the ASAP backlog is shrinking.
  3. Check the Ad hoc tasks page: the queue should go down over the following runs.
  4. Trigger something users rely on, such as a forum post with notifications, and confirm the email arrives.
  5. Leave the log redirection on for a day and check it for PHP errors before you switch it back to /dev/null.

Preventing it from happening again

  • Keep cron in your deployment checklist. Every server move, rebuild and major upgrade should end with a check that the crontab exists, uses the correct project root path and the correct PHP binary.
  • Monitor the status check. admin/cli/checks.php returns NRPE-style exit codes (1 for a warning, 2 for critical), so a monitoring agent can run it and alert you, which beats waiting for users to report missing emails.
  • Pair every disable with an enable. Script cron.php --disable-wait and --enable together around maintenance.
  • On clusters, use dedicated task runners. Run cron on one or more nodes that do not serve users, and follow Moodle's own advice for keep alive: 0 with dedicated task runners, otherwise a value similar to the ad hoc task concurrency limit, never above the 15 minute maximum.
  • Watch the fail delay and ad hoc queue checks. The System status report also has "Tasks max fail delay", "Ad hoc task queue" and "Long running tasks" checks; treat warnings there as early signs of a task that will block others.

For how cron fits into the rest of a Moodle stack, including upgrades and support dates, see our Moodle guide.

How we can help

We run Moodle sites day to day: cron and task runner setup, cluster locking, upgrades across the 5.1 directory change and monitoring that catches a stopped cron before users do. See our Moodle services and LMS maintenance and support, or contact us with what your notifications page shows.

Frequently asked questions

How often should Moodle cron run?

Every minute. Moodle's Task API documentation recommends running cron at least once per minute, because scheduled and ad hoc tasks only run as often as cron does.

Where is the Moodle cron script in Moodle 5.1 and later?

In the project root at admin/cli/cron.php. Since 5.1 the web server serves the public/ directory, but the CLI scripts stay in the project root, so do not point the crontab into public/.

Why does Moodle say cron has never been run?

Moodle has no record of cron starting, which usually means there is no system cron entry for the site, or the entry fails before Moodle loads, for example because of a wrong path or PHP binary. Run the script by hand as the web server user to see the error.

How do I run a single Moodle scheduled task manually?

From the project root run php admin/cli/scheduled_task.php --execute='\core\task\session_cleanup_task' as the web server user, replacing the class with the task you need. Add --showdebugging to see errors. Use --list to see all scheduled tasks.

Is it normal to see several cron.php processes running?

Yes. Each cron process keeps polling for new tasks for the keep alive time, three minutes by default, so with a one-minute schedule a few overlap. The maximum keep alive is 15 minutes.

Free 30-minute consultation

Not sure where to start? Talk to an LMS engineer.

Tell us what platform you run and what you need. You’ll get honest, practical advice - even if the answer is that you don’t need us.

Sources & references

  1. Task API - Moodle HQ
  2. Adhoc tasks - Moodle HQ
  3. admin/cli/cron.php (Moodle 5.3.0) - Moodle HQ
  4. admin/cli/scheduled_task.php (Moodle 5.3.0) - Moodle HQ
  5. Cron running status check (Moodle 5.3.0) - Moodle HQ
  6. Directory restructure in Moodle 5.1 - Moodle HQ
Add as a preferred source on Google

Add All-in-One LMS as a preferred source to see more of our articles in Google’s Top Stories.

Learn more about this topic with AI

Gemini opens with the prompt copied - paste it into the chat to start.

Keep reading

Illustration of five ranked cards with the top card starred, representing a Moodle theme comparison for institutions in 2027

· 15 min read

Moodle Themes for 2027, Ranked for Institutions

Eleven free and premium Moodle themes scored for universities, colleges and training providers on 5.3 LTS readiness, upgrade safety, accessibility, b…

Before you go - get a free LMS audit

Tell us which LMS you use and what is not working. An engineer replies within one business day.

Choose which optional cookies we may use. You can change this at any time from "Cookie preferences" in the footer. Cookie policy · Privacy policy