Understanding WP-Cron

WP-Cron or WordPress cron is the system built into WordPress that handles the scheduling of time-based tasks. Out-of-the-box, WordPress performs a number of scheduled tasks, they include:

  • WordPress core update checks
  • Plugin update checks
  • Theme update checks
  • Publishing of scheduled posts

Plugins can also utilize WP-Cron to schedule additional tasks, such as a backup plugin scheduling recurring database backups. Therefore, it’s imperative that WP-Cron is functioning correctly.

WP-Cron takes its name from cron, which is a time-based job scheduler in Unix-like systems. However, because WordPress has very few requirements, WP-Cron is designed to work on any hosting provider (including shared hosting) without using any external software or tools for scheduling events.

For WP-Cron to schedule tasks without using external software, the cron schedule has to be checked during the lifecycle of a WordPress request. The flow looks something like this:

  1. During the init hook, WP-Cron checks the database for scheduled events
  2. If scheduled events are due, a HTTP request is spawned to the wp-cron.php file
  3. wp-cron.php processes the hooks associated with the scheduled task

Although WP-Cron makes it possible to schedule events in WordPress, it’s not without its own issues.

WP-Cron is Unreliable

For WP-Cron to fire scheduled events, the WordPress site must receive regular traffic. This can be problematic for low-traffic sites and cause scheduled events to be missed if no traffic is received for a prolonged period of time.

Likewise, high-traffic sites which are heavily page cached can also run into the same scheduling issues. Page caching essentially turns WordPress into a static HTML site, which means that requests no longer execute WordPress and can no longer fire off the scheduled events.

WP-Cron is Inefficient

For high-traffic sites that can’t be page cached, WP-Cron is extremely inefficient. This is because WP-Cron will check for scheduled events on every page load. This could mean that the cron schedules are being checked multiple times per second. Most scheduled events don’t need this level of granularity, as scheduled events typically run on the minute. Therefore, checking for scheduled events on every single page load is unnecessary.

A common misconception is that every page load results in a HTTP request to wp-cron.php, but thankfully this isn’t the case. WordPress has a rudimentary locking system in place, which attempts to prevent calls to wp-cron.php from occurring more than once in a sixty-second period.

Whenever a request is spawned to wp-cron.php a lock is created (stored as a WordPress transient) which stores the current Unix timestamp. WordPress will no longer spawn additional requests to wp-cron.php until 60 seconds have passed. This system does introduce a level of atomicity, but it isn’t foolproof. High-traffic sites receiving multiple simultaneous requests can still spawn multiple requests to wp-cron.php, thus increasing server load.

How to Disable WP-Cron

To disable WP-Cron, add the following constant to your wp-config.php file.

define( 'DISABLE_WP_CRON', true );

This will prevent WP-Cron from automatically checking for scheduled cron events on every page load. This doesn’t disable scheduled events in WordPress completely, only the automatic checking and firing of scheduled events. To ensure that scheduled events continue to work you need an alternative method of firing the scheduled events. For multisite installs, each subsite needs its own alternative method of firing the scheduled events.

Unix Cron

In Unix-like systems, cron is a system daemon that runs in the background. Unlike WP-Cron, Unix cron runs continuously and uses the system clock to schedule tasks. This means that scheduled events are never missed.

We can use a Unix cron in conjunction with WP-CLI to reliably schedule events. Every site created in SpinupWP has the following entry added to the site user’s crontab:

* * * * * cd /sites/acmepublishing.com/files/; /usr/local/bin/wp cron event run --due-now >/dev/null 2>&1

This will run scheduled events every minute. If you would like to add additional cron jobs or edit an existing cron job, check out our doc on how to add new cron jobs.

Testing Cron

When using the DISABLE_WP_CRON constant it’s important to verify that scheduled events are still working. The easiest way to do this is by using WP-CLI.

wp cron event list
+----------------------+----------------------+---------------------------+------------+
| hook                 | next_run_gmt         | next_run_relative         | recurrence |
+----------------------+----------------------+---------------------------+------------+
| wp_privacy_delete_ol | 2020-07-17 10:51:51  | now                       | 1 hour     |
| d_export_files       |                      |                           |            |
| recovery_mode_clean_ | 2020-07-17 20:51:51  | now                       | 1 day      |
| expired_keys         |                      |                           |            |
| wp_version_check     | 2020-07-17 20:51:51  | now                       | 12 hours   |
| wp_update_plugins    | 2020-07-17 20:51:51  | now                       | 12 hours   |
| wp_update_themes     | 2020-07-17 20:51:51  | 10 hours 34 minutes       | 12 hours   |
| wp_scheduled_delete  | 2020-07-17 20:51:53  | 10 hours 34 minutes       | 1 day      |
| delete_expired_trans | 2020-07-17 20:51:53  | 10 hours 34 minutes       | 1 day      |
| ients                |                      |                           |            |
| wp_scheduled_auto_dr | 2020-07-17 20:51:55  | 10 hours 34 minutes       | 1 day      |
| aft_delete           |                      |                           |            |
| wp_site_health_sched | 2020-07-21 20:51:51  | 4 days 10 hours           | 1 week     |
| uled_check           |                      |                           |            |
+----------------------+----------------------+---------------------------+------------+

If the next_run_relative column contains several entries with a value of now the chances are that cron events aren’t being scheduled often enough (if at all). You can roughly work out when cron events were last scheduled by comparing the next_run_gmt column minus the recurrence column to the current time in GMT.

If you’d prefer to verify that cron events are working via the WordPress dashboard, you can do so using the WP Crontrol plugin.

Managing Active Cron Jobs

Every site you add to a server will result in a new instance of WP-Cron being triggered on that server. Depending on the number of scheduled tasks you have for each site, this could lead to cron related CPU spikes, which may cause timeouts or downtime for other sites on that server. Increasing the cron interval for sites that don’t have many scheduled tasks can help to reduce these CPU spikes.