2 min readWordPress
SAMPLEWP-Cron vs Action Scheduler: when scheduled jobs need a real queue
WP-Cron only runs when someone visits your site, and it fails silently. Here's when that's fine, when it isn't, and how Action Scheduler fixes it.
WP-Cron is not cron. It's a check that runs on page load: when a visitor arrives, WordPress looks for overdue events and fires them. That one design decision explains most "my scheduled task didn't run" bugs.
Where WP-Cron breaks down#
- Low traffic means late jobs. No visitors, no execution. A renewal reminder due at 9:00 might run at 14:30.
- High traffic means overlapping runs. Several requests can pick up the same event before it's marked done.
- Failures are invisible. If a callback throws, there's no retry and no record.
php
// Classic WP-Cron: fine for "clear a transient once a day".
if ( ! wp_next_scheduled( 'myplugin_daily_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'myplugin_daily_cleanup' );
}
add_action( 'myplugin_daily_cleanup', 'myplugin_cleanup_expired_tokens' );Action Scheduler: a queue with a paper trail#
Action Scheduler, the library WooCommerce uses, stores every job as a database row with a status: pending, in-progress, complete, failed. You can see what ran, what failed and why, under Tools → Scheduled Actions.
php
// One-off background job: runs as soon as possible, off the request.
as_enqueue_async_action( 'myplugin_sync_order', array( 'order_id' => $order_id ), 'myplugin' );
// Recurring job, guarded so it's only registered once.
if ( false === as_has_scheduled_action( 'myplugin_hourly_sync', array(), 'myplugin' ) ) {
as_schedule_recurring_action( time(), HOUR_IN_SECONDS, 'myplugin_hourly_sync', array(), 'myplugin' );
}Trigger the queue from a real cron#
On anything that matters, stop relying on visitors. Disable the page-load trigger and run due events from the server's crontab:
php
// wp-config.php
define( 'DISABLE_WP_CRON', true );bash
# crontab -e: run due events every minute with WP-CLI
* * * * * cd /var/www/site && wp cron event run --due-now --quietRule of thumb#
| Job | Use |
|---|---|
| Housekeeping that can run late | WP-Cron |
| Anything touching money, email or third-party APIs | Action Scheduler |
| Large batches (imports, syncs) | Action Scheduler, chunked into small jobs |
#wordpress#background-jobs#action-scheduler
Related case studyStripe Elements checkout for a membership plugin