A post scheduled to publish at a specific time doesn’t go live when expected. A plugin’s recurring email notification never arrives. This tends to happen on low-traffic sites, and there’s a specific reason for it.
Note: WP-Cron is WordPress’s built-in scheduling system. It sounds like the OS-level
crondaemon, but the underlying mechanism is quite different.
WordPress’s WP-Cron doesn’t work like a real OS cron daemon. On every page load, WordPress checks whether any scheduled task is past its due time and, if so, runs it. This is what’s known as “pseudo-cron” — and its weakness is that nothing runs without a page visit. Schedule a post to publish at 3am on a site with little overnight traffic, and the publish task can sit unexecuted until the next visitor happens to load a page.
WP-CLI lets you look inside this otherwise invisible system and run exactly the task you need, right now.
Listing what’s scheduled
wp cron event list
| hook | next_run_gmt | recurrence |
|---|---|---|
| publish_future_post | 2026-06-20 03:00:00 | – |
| wp_version_check | 2026-06-20 06:12:00 | 12 hours |
| wp_scheduled_delete | 2026-06-21 00:00:00 | daily |
hook is the task’s identifier, next_run_gmt is the next scheduled run time in UTC, and recurrence is the repeat interval. If publish_future_post is still listed despite its time having already passed, that confirms the task is overdue simply because no page load has triggered it yet.
Running a task right now
To trigger a specific task immediately:
# Run a specific hook right now
wp cron event run publish_future_post
# Run every overdue task at once
wp cron event run --due-now
--due-now finds every task whose scheduled time has passed but hasn’t run yet, and executes all of them. Instead of waiting for a visitor to trigger the check, this one command runs the post publish, the email notification, or whatever else is pending.
Confirming WP-Cron itself is working
wp cron test
This checks whether the WP-Cron scheduler is functioning at all. On sites where wp-config.php has define('DISABLE_WP_CRON', true); set, the pseudo-cron mechanism is disabled entirely, and something else — a manual call or an external scheduler — has to call wp cron event run --due-now on a regular basis.
Checking available recurrence intervals
wp cron schedule list
| name | display | interval (seconds) |
|---|---|---|
| hourly | Once Hourly | 3600 |
| twicedaily | Twice Daily | 43200 |
| daily | Once Daily | 86400 |
Plugins that register custom intervals (e.g. every 15 minutes) show up here too. This is also where an unexpectedly frequent task can be spotted.
Switching to a real cron
Setting DISABLE_WP_CRON to true and calling WP-CLI from the server’s actual crontab instead removes the dependency on page visits entirely — tasks then run at a guaranteed time.
# crontab example — every 5 minutes
*/5 * * * * cd /path/to/wordpress && wp cron event run --due-now > /dev/null 2>&1
The more common alternative — hitting wp-cron.php over HTTP with curl on a schedule — works too, but calling WP-CLI directly skips the HTTP overhead and avoids exposing wp-cron.php as a publicly reachable endpoint that has to be hit from outside the server.
Verifying after running
# Confirm the scheduled post is now published
wp post list --post_status=publish --orderby=date --order=DESC --posts_per_page=3
# Confirm no overdue tasks remain
wp cron event list
If publish_future_post no longer appears in the list, the corresponding post has already gone through its publish step.
Summary
| What you need | Command |
|---|---|
| List scheduled tasks | wp cron event list |
| Run a specific task now | wp cron event run <hook> |
| Run every overdue task | wp cron event run --due-now |
| Confirm WP-Cron is working | wp cron test |
| List available recurrence intervals | wp cron schedule list |
Knowing that WP-Cron is a page-visit-dependent pseudo-cron is what makes wp cron event list the first move when a scheduled publish doesn’t go through. Like recovering from a wp-admin lockout or serialization-safe replacement, this is WP-CLI reaching past the admin dashboard to the mechanism actually running underneath it.