Running WordPress cron on a multisite instance

For a long time I used the WP Cron Control plugin and an associated cron job to make sure that scheduled actions on my WordPress multisite instance were executed properly. (You should never rely on event execution that is triggered by visits to your website, the WordPress default, IMHO.) But after the upgrade to WordPress 5.4 I noticed that some of my scheduled events in WordPress were not firing on time, sometimes delayed by 10-20 minutes. I did some troubleshooting and got as far as suspecting a weird interaction between that plugin and WordPress 5.4, but never got to the bottom of it.

When I reluctantly went in search of a new solution, I decided to try using WP CLI cron commands, executed via my server’s own cron service. Ryan Hellyer provided most of what I needed in this helpful post, and I extended it a bit for my own purposes.

Here’s the resulting script that I use:

#!/bin/bash

# This script runs all due events on every WordPress site in a Multisite install, using WP CLI.
# To be run by the "www-data" user every minute or so.
#
# Thanks https://geek.hellyer.kiwi/2017/01/29/using-wp-cli-run-cron-jobs-multisite-networks/

PATH_TO_WORDPRESS="/path/to/wordpress"
DEBUG=false
DEBUG_LOG=/var/log/wp-cron

if [ "$DEBUG" = true ]; then
        echo $(date -u) "Running WP Cron for all sites." >> $DEBUG_LOG
fi

for URL in = $(wp site list --field=url --path="/path/to/wordpress" --deleted=0 --archived=0)
do
        if [[ $URL == "http"* ]]; then
                if [ "$DEBUG" = true ]; then
                        echo $(date -u) "Running WP Cron for $URL:" >> $DEBUG_LOG
                        wp cron event run --due-now --url="$URL" --path="$PATH_TO_WORDPRESS" >> $DEBUG_LOG
                else
                        wp cron event run --quiet --due-now --url="$URL" --path="$PATH_TO_WORDPRESS"
                fi
        fi
done

Then, in my system crontab:

# Run WordPress Cron For All Sites
*/2 * * * * www-data /bin/bash /path/to/bin/run-wp-cli-cron-for-sites.bash

Yes, I run cron every 2 minutes; there are some sites I operate that require very precise execution times in order to be useful. One implication is that this solution does not scale up very well; if the total execution time of all cron jobs across all sites exceeds 2 minutes, I could quickly run into situations where duplicate jobs are running trying to do the same thing, and that could be bad for performance or worse.