Skip to main content
Blog|
How-to guides

How to Run a WP-CLI Command with Cron

|
Apr 22, 2026|12 min read
HOW-TO GUIDESHow to Run a WP-CLI Commandwith CronHOSTNEYhostney.comMarch 19, 2026

WordPress has a built-in task scheduler called wp-cron. It handles scheduled events like publishing future-dated posts, checking for plugin updates, running scheduled backups, and processing WooCommerce orders. The problem is how it works: wp-cron runs by piggybacking on visitor page loads. Every time someone visits your site, WordPress checks if any scheduled tasks are due and fires them.

This design has real consequences. On low-traffic sites, tasks run late because nobody visits to trigger them. On high-traffic sites, wp-cron adds an extra PHP execution on nearly every page load, consuming PHP-FPM workers that should be serving actual visitors. The fix is straightforward: disable wp-cron’s web-triggered behavior and replace it with a real system cron job that runs WP-CLI on a schedule.

The problem with wp-cron.php#

When a visitor loads any page on your WordPress site, WordPress makes an asynchronous HTTP request to wp-cron.php to check for and run pending scheduled tasks. This happens on every page load, regardless of whether any tasks are due.

Why this is wasteful#

Each call to wp-cron.php is a full PHP execution. It loads the entire WordPress stack, checks the database for pending events, and runs any that are due. On a site with 1,000 daily visitors, that is up to 1,000 extra PHP executions per day just to check a scheduler. The actual scheduled tasks might only need to run a handful of times.

On shared hosting or servers with limited PHP-FPM workers, these extra executions compete with real page loads. During traffic spikes, the additional wp-cron.php requests can push the server past its PHP worker limit, resulting in 503 errors for actual visitors.

Why tasks run late on low-traffic sites#

If nobody visits your site for six hours, no scheduled tasks run for six hours. A post scheduled to publish at 8:00 AM does not actually publish until the next visitor arrives, which might be 10:00 AM. A backup plugin scheduled for 3:00 AM never runs at 3:00 AM because nobody is browsing your site at that hour.

This is a fundamental limitation of the web-triggered design. wp-cron has no independent process keeping time. It relies entirely on traffic to trigger it.

What WP-CLI is#

WP-CLI is the command-line interface for WordPress. It lets you manage WordPress from the terminal without using a browser. You can update plugins, manage users, import content, run database operations, and interact with WordPress’s internal APIs directly from the command line. If you do not have WP-CLI on the server yet (or have never used it for the install itself), how to install WordPress with WP-CLI covers both the WP-CLI install and the everyday command set.

For the cron replacement, the relevant command is:

wp cron event run --due-now

This does exactly what wp-cron.php does on a page load: it checks for scheduled events that are due and runs them. The difference is that it runs as a CLI process, not as a web request. It does not consume a PHP-FPM worker, does not compete with visitor traffic, and runs on a schedule you control.

Installing WP-CLI#

If WP-CLI is not already installed on your server:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

Verify:

wp --info

This prints the WP-CLI version, PHP version, and other details. If it works, WP-CLI is ready.

Step 1: Disable wp-cron.php#

Open wp-config.php in your WordPress root directory and add:

define('DISABLE_WP_CRON', true);

This must go before the line that says /<em> That's all, stop editing! </em>/ .

What this does: it tells WordPress to stop making the asynchronous HTTP request to wp-cron.php on every page load. Scheduled events are still registered in the database. They just do not get triggered automatically by web traffic anymore. You are taking over responsibility for triggering them.

What this does not do: it does not remove wp-cron.php from the server. The file still exists and still responds to direct HTTP requests. It just stops being called automatically.

Step 2: Set up the cron job#

The crontab entry#

Open the crontab:

crontab -e

Add this line:

*/5 * * * * cd /var/www/html && /usr/local/bin/wp cron event run --due-now --quiet >> /var/log/wp-cron.log 2>&1

Save and exit. The cron daemon picks up the change immediately.

Breaking down the command#

<em>/5 </em> <em> </em> * – run every 5 minutes. This is the standard interval. WordPress’s built-in scheduler checks on every page load, so every 5 minutes is more than adequate for most sites. See Cron job syntax: a practical guide with examples for the full explanation of the five-field syntax.

cd /var/www/html – change to the WordPress directory. WP-CLI needs to be run from the WordPress root, or you need to specify --path= . Using cd is simpler.

&& – only run the next command if cd succeeds. If the directory does not exist (renamed, unmounted), the WP-CLI command is not executed.

/usr/local/bin/wp – the full path to WP-CLI. Cron jobs run with a minimal PATH that may not include /usr/local/bin . Always use the full path.

cron event run --due-now – run all scheduled events that are currently due. Events that are not yet due are left alone.

--quiet – suppress non-error output. Without this, every successful run generates output that cron tries to email.

>> /var/log/wp-cron.log 2>&1 – append both standard output and error output to a log file. This captures any errors so you can debug if something goes wrong.

Which user should the cron job run as?#

The cron job should run as the same user that owns the WordPress files, which is typically www-data on Ubuntu/Debian:

sudo crontab -u www-data -e

Running as the wrong user causes permission issues. If WP-CLI runs as root but WordPress files are owned by www-data , any files WP-CLI creates (cache files, upload directories) will be owned by root. WordPress (running as www-data through PHP-FPM) cannot write to them, leading to silent failures.

If you are unsure which user owns your WordPress files:

ls -la /var/www/html/wp-config.php

The third column is the owner. Use that user for the cron job.

Alternative: using -path instead of cd#

*/5 * * * * /usr/local/bin/wp --path=/var/www/html cron event run --due-now --quiet >> /var/log/wp-cron.log 2>&1

The --path flag tells WP-CLI where WordPress is installed. This is equivalent to the cd approach but keeps the command on one line without chaining.

Step 3: Verify it works#

Check that scheduled events are running#

After setting up the cron job, wait at least 5 minutes, then check:

wp cron event list --path=/var/www/html

This shows all registered scheduled events, their schedule, the next run time, and whether they have run recently. Events that should have run in the last 5 minutes should show an updated “next run” time.

Check the log file#

tail -20 /var/log/wp-cron.log

If the cron job is working, the log file contains output from each run. With --quiet , successful runs produce no output, so an empty or absent log file after several runs means everything is working. Errors are still logged.

Check the system cron log#

To confirm that the cron daemon is executing the job:

grep wp /var/log/syslog

Or on CentOS/RHEL:

grep wp /var/log/cron

You should see entries showing the cron job executing every 5 minutes.

Test manually#

Run the command manually to confirm it works before relying on cron:

cd /var/www/html && sudo -u www-data /usr/local/bin/wp cron event run --due-now

If this produces errors, fix them before adding the cron job. Common issues:

  • WP-CLI cannot find WordPress (wrong path)
  • Database connection fails (wp-config.php not readable by the user)
  • Permission errors (running as wrong user)

Running specific WP-CLI commands on a schedule#

The cron replacement above handles WordPress’s internal scheduled events. But you can also schedule any WP-CLI command to run on a cron schedule.

Database optimization#

0 3 * * 0 cd /var/www/html && /usr/local/bin/wp db optimize --quiet >> /var/log/wp-db-optimize.log 2>&1

Runs OPTIMIZE TABLE on all WordPress tables every Sunday at 3:00 AM. This reclaims unused space and defragments tables after heavy write activity. For a full database cleanup routine that combines transient deletion, revision pruning, spam removal, and table optimization into a single scheduled script, see WordPress database optimization: how to clean up and speed up your database.

Delete spam comments#

0 4 * * * cd /var/www/html && /usr/local/bin/wp comment delete $(/usr/local/bin/wp comment list --status=spam --format=ids --path=/var/www/html) --force --quiet 2>> /var/log/wp-spam-cleanup.log

Deletes all spam comments daily at 4:00 AM. Without this, spam comments accumulate in the database and slow down queries.

Delete post revisions#

0 5 1 * * cd /var/www/html && /usr/local/bin/wp post delete $(/usr/local/bin/wp post list --post_type=revision --format=ids --path=/var/www/html) --force --quiet 2>> /var/log/wp-revision-cleanup.log

Deletes all post revisions on the 1st of every month. On sites with heavy editing, revisions can make up the majority of database rows – WordPress revisions: how to use and control them covers the whole picture, including the WP_POST_REVISIONS limit so new revisions do not simply refill what this job deletes.

Update plugins#

0 6 * * 1 cd /var/www/html && /usr/local/bin/wp plugin update --all --quiet >> /var/log/wp-plugin-updates.log 2>&1

Updates all plugins every Monday at 6:00 AM. Use this with caution. Automatic updates can break a site if a plugin update introduces a bug or incompatibility. Only set this up if you have a monitoring system that catches failures quickly, or if you are running it on a staging environment first. WordPress’s built-in per-plugin auto-update toggle is usually the safer choice unless you specifically need cron-driven control.

Flush object cache#

*/30 * * * * cd /var/www/html && /usr/local/bin/wp cache flush --quiet 2>> /var/log/wp-cache-flush.log

Flushes the object cache every 30 minutes. This is a blunt instrument and usually not necessary. It is sometimes useful when debugging cache-related issues or when a plugin does not invalidate its cache properly.

Run all examples as the correct user#

All the above examples should run as the WordPress file owner. If using sudo crontab -u www-data -e , the jobs run as www-data automatically. If using the root crontab with the system crontab format ( /etc/crontab ), add the username:

0 3 * * 0 www-data cd /var/www/html && /usr/local/bin/wp db optimize --quiet

Multisite considerations#

On a WordPress multisite installation, WP-CLI commands default to the main site. To run cron events for all sites in the network:

*/5 * * * * cd /var/www/html && /usr/local/bin/wp site list --field=url | xargs -I {} /usr/local/bin/wp cron event run --due-now --url={} --quiet 2>> /var/log/wp-cron.log

This lists all site URLs in the multisite network and runs due cron events for each one. Without specifying --url , only the main site’s events are processed.

WP-CLI cron command reference#

Once you have WP-CLI working against your install, the wp cron family gives you complete control over what WordPress has scheduled. The subcommands split into two groups: wp cron event (individual scheduled tasks) and wp cron schedule (the recurrence intervals available).

Default WordPress cron schedules#

WordPress ships with three built-in recurrence intervals. Plugins register their tasks against these (or define their own custom ones).

Schedule nameIntervalTypical use
hourly 3600 secondsCache warming, log cleanup, health checks
twicedaily 43200 seconds (12 hours)Plugin update checks, WordPress core update pings
daily 86400 seconds (24 hours)Automatic database cleanup, nightly cron jobs registered by plugins

Some plugins register additional intervals like weekly , monthly , or custom ones like every_fifteen_minutes . List everything registered on your install:

wp cron schedule list

This shows every interval name, its display label, and its duration in seconds. If a plugin expected a weekly schedule and it is not in the list, the plugin did not register it correctly – which usually means the task silently never runs.

Common wp cron event commands#

# List every scheduled event with its next run time
wp cron event list

# List only events that are due right now
wp cron event list --due-now

# Run all due events immediately (this is the one the crontab uses)
wp cron event run --due-now

# Run a specific event by hook name
wp cron event run wp_version_check

# Schedule a new event
wp cron event schedule my_custom_hook now hourly

# Delete a scheduled event (useful when a removed plugin left orphan events)
wp cron event delete my_custom_hook

# Check whether wp-cron is even working
wp cron test

The wp cron event list output is the single most useful command when debugging “why did my scheduled post not publish.” If the event is listed with a past timestamp in the “Next Run” column and you have DISABLE_WP_CRON set, your system cron is not actually running – double-check the crontab. If the event is not listed at all, the plugin that was supposed to register it failed silently.

Orphaned events from removed plugins#

When you delete a plugin that registered custom cron events, WordPress does not automatically clean up the scheduled tasks – they stay in the database and keep failing with “action not found” errors on every cron run. To find and remove them:

# Show all events, look for hooks with unrecognized names
wp cron event list

# Remove the orphan
wp cron event delete orphan_hook_name

This is the cleanup step most “why is my error log full of cron failures” threads never mention.

Troubleshooting#

"Error: This does not appear to be a WordPress installation."#

WP-CLI cannot find WordPress at the current directory or the specified --path . Check:

ls /var/www/html/wp-config.php

If the file does not exist at that path, update the path in the cron job.

"Error establishing a database connection"#

The database is not accessible from the CLI context. This can happen if wp-config.php uses localhost for the database host and the MySQL socket is not in the expected location. Check:

cd /var/www/html && wp db check

"Permission denied" errors#

The cron job is running as a user that does not have read access to the WordPress files. Check file ownership and ensure the cron job runs as the correct user.

Tasks still not running on time#

After setting up the cron job, if tasks are still delayed:

  1. Confirm DISABLE_WP_CRON is set to true in wp-config.php (not false or missing)
  2. Confirm the cron job is in the crontab: crontab -l
  3. Check the system log for execution: grep CRON /var/log/syslog
  4. Run the command manually to see if it produces errors

WP-CLI uses a different PHP version#

WP-CLI uses whatever php binary is in the PATH. If your server has multiple PHP versions and WP-CLI picks the wrong one, specify the PHP binary explicitly:

*/5 * * * * cd /var/www/html && /usr/bin/php8.3 /usr/local/bin/wp cron event run --due-now --quiet >> /var/log/wp-cron.log 2>&1

WordPress cron on Hostney#

On Hostney, cron jobs are managed through the control panel under the Cron Jobs section. Currently, Hostney’s cron scheduler supports web-based calls (HTTP requests to a URL), not direct CLI commands. This means you cannot run WP-CLI directly from the cron scheduler.

To replace wp-cron.php with a scheduled cron job on Hostney:

  1. Add define('DISABLE_WP_CRON', true); to wp-config.php
  2. Go to Cron Jobs in the control panel
  3. Set the schedule to every 5 minutes
  4. Set the URL to: https://yourdomain.com/wp-cron.php?doing_wp_cron

This triggers wp-cron.php on a fixed schedule via HTTP request rather than relying on visitor traffic. The result is the same: scheduled tasks run reliably every 5 minutes regardless of site traffic. The difference from the WP-CLI approach described above is that the request goes through the web server and consumes a PHP-FPM worker briefly, but since it runs on a fixed interval rather than on every page load, the overhead is minimal.

For SSH access to your container (useful for running WP-CLI commands manually), see the Terminal Access section in the control panel.

Related articles