All guides

Site health

WordPress cron not running: scheduled posts, backups and the four reasons WP-Cron stalls

How WP-Cron actually works, the four reasons scheduled tasks stop running, how to diagnose which one you have in a click, and how to replace the built-in scheduler with a real system cron.

5 min readUpdated

How WP-Cron actually works

WordPress has no scheduler of its own. "WP-Cron" is a list of due tasks that is checked at the start of ordinary page requests: when a visitor loads a page and something is overdue, WordPress fires a background request to wp-cron.php on itself to run the queue. That design has three consequences that explain almost every cron problem:

  1. No visitors, no cron. A site with little traffic runs scheduled tasks late or never. Overnight backups on a site nobody visits overnight do not happen.
  2. It depends on the site being able to request itself. If that loopback request fails (basic auth on staging, a firewall, a DNS quirk inside the container), nothing runs and nothing tells you.
  3. One run at a time. A lock stops two runs overlapping. If a task fatals mid-run, the lock can be left behind and the queue looks busy while doing nothing.

Symptoms: "Missed schedule" on scheduled posts, backups that stopped weeks ago, an ecommerce site whose abandoned-cart or subscription renewals are not firing, cache preloading that never completes, a "overdue" warning from a plugin.

See what is queued and what is overdue

Install Dragon Cron Manager and open Tools, then Cron Manager. Every scheduled event is listed with its hook, schedule, next run time and arguments, and overdue ones are flagged in the health bar.

Every scheduled task in one place, overdue events flagged, with Run, Test and Trash on each.
Every scheduled task in one place, overdue events flagged, with Run, Test and Trash on each.

Per event you can Run it now (and reschedule the next run), Test it without touching the schedule, or Trash it. Trash is a 30-day bin, so removing a stray event left behind by a deleted plugin is reversible. The Run Log records automatic runs as well as manual ones, with duration and a success or failure flag, and a Source column that says which was which. That log is how you will confirm the fix later.

Diagnose which of the four problems you have

Press Diagnose in the health bar. The cron doctor runs a live root-cause check rather than telling you "cron is overdue" and leaving you to guess. It comes back with one of four findings:

1. DISABLE_WP_CRON is set, but nothing is firing

Someone (often the host, sometimes a previous developer) added define( 'DISABLE_WP_CRON', true ); to wp-config.php, which is correct only if a real system cron was set up to replace it. It was not, or it stopped. The diagnosis prints the exact crontab line to add. See the permanent fix below.

2. The site cannot reach its own wp-cron.php

The loopback request fails. The doctor shows you the actual error: an HTTP 401 means basic auth is in front of the site (common on staging), a timeout or connection refused points at a firewall or a host that blocks loopback, and a DNS error means the server cannot resolve its own domain from inside. Each has a different fix: exclude wp-cron.php from the auth rule, allow the loopback in the firewall, or add the domain to the server's hosts file. The permanent fix below sidesteps all three.

3. A crashed run is holding the lock

A task fataled part-way and the lock was never released, so every subsequent run thinks one is already in progress. The diagnosis points you at the Run Log entry that failed. Fix or remove the task that crashed (Test it in isolation), and the lock clears.

4. Everything works, the queue is just starved

Loopback is fine, nothing is locked, there simply have not been enough visits to trigger runs. This is the low-traffic case, and the diagnosis kicks the queue for you on the spot. It is also the strongest argument for the permanent fix.

The permanent fix: a real system cron

Whatever the diagnosis, the durable answer is the same: stop relying on visitors and let the server's own scheduler trigger the queue every few minutes.

  1. In wp-config.php, above the "That's all, stop editing" line, add:
php
define( 'DISABLE_WP_CRON', true );
  1. Add a system cron entry that runs the queue. Either of these, every five minutes:
text
*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
text
*/5 * * * * cd /var/www/example.com && wp cron event run --due-now --quiet

The WP-CLI form does not depend on the site being reachable over HTTP at all, which makes it immune to the loopback problems above. Most hosting panels have a "Cron Jobs" screen that takes the same command.

  1. Back in Cron Manager, watch the Run Log for entries with Source Automatic arriving on schedule. If they appear, you are done. If they do not, the cron entry itself is not firing: check the panel's cron log, or that curl or wp is on the cron user's PATH.

Housekeeping while you are here

  • Trash orphaned events. Plugins you removed long ago often leave hooks scheduled forever. They fail silently on every run. Trash them; the 30-day bin has your back.
  • Use Test before Run on anything you do not recognise. Test executes the task without rescheduling it, so a broken task cannot be pushed further into the future by accident.
  • Add events without code. The Add Event button schedules a new hook with a single or recurring schedule, first-run time and JSON arguments, which is handy for one-off maintenance tasks.
  • Check Schedules to see every registered interval. A plugin that registers a "every minute" schedule on a busy site deserves a second look.

If the tasks that are not running are the ones that send email, check delivery too: WordPress not sending email. And if cron runs are slow enough to overlap, find out which plugin is doing it.

Checklist

  • Overdue events visible in Cron Manager and understood
  • Diagnose run and the finding acted on
  • DISABLE_WP_CRON set in wp-config.php
  • System cron entry added at a 5 minute interval
  • Run Log shows Automatic entries on schedule
  • Orphaned events trashed

Site health

How to find and fix broken links in WordPress

Find broken internal, external and image links across a WordPress site, triage them without false alarms, fix them safely in the block editor, and stop new ones appearing.

5 min readRead