Skip to content
ExponaBLOG
WORDPRESS DEVELOPMENT & PLUGINS · DAY 098 / 100

Cron alternatives for busy sites for WordPress developers

Reliable scheduling.

Expona Admin7 min read

Introduction

WordPress's built-in WP-Cron is convenient during development, but on busy sites handling thousands of requests per day, it becomes a bottleneck that can silently fail. We'll explore cron alternatives for busy sites for WordPress developers, covering reliable scheduling strategies that keep your background tasks running whether your traffic spikes or your site sits idle.

When you're running a high-traffic e-commerce store, a SaaS platform built on WordPress, or any site where scheduled tasks matter, the default WP-Cron behaviour—triggering on visitor activity—is risky. We've learned this the hard way in production. This guide walks through proven alternatives that teams use to ensure emails send, inventory updates process, and reports generate on schedule.

DISABLE_WP_CRON and a server cron

The most straightforward and reliable approach is to disable WordPress's loopback-based cron and replace it with a real server cron job. This removes the dependency on site traffic and ensures your scheduled tasks run at predictable intervals.

First, disable WP-Cron in your wp-config.php:

php
define( 'DISABLE_WP_CRON', true );

This prevents WordPress from attempting to spawn cron events on page requests. Next, set up a real cron job on your server that hits the WordPress loopback endpoint every five minutes:

php
*/5 * * * * curl https://yoursite.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

Or, if your hosting restricts direct calls, you can use the WordPress REST API with authentication:

php
*/5 * * * * curl -s https://yoursite.com/wp-json/wp/v2/users/me \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" > /dev/null 2>&1

This approach works well for shared hosting and VPS environments where you have SSH access. The five-minute interval is a reasonable default for most sites; adjust it based on your task frequency needs. On AWS, Lightsail, or other managed environments, this is often your starting point.

One real-world scenario: we had a WooCommerce store that processed order status updates every 10 minutes. With loopback cron, these would fail silently during the night when traffic was low. Switching to server cron cut failed updates to zero because the task ran regardless of visitor activity.

Action Scheduler for queues

For more complex scheduling—especially when you need job retries, failure handling, and visibility into what's queued—Action Scheduler is a production-grade library trusted by WooCommerce, Jetpack, and dozens of major plugins.

Action Scheduler is a queuing system built for WordPress. It handles recurring tasks, one-off scheduled actions, and async processing with built-in logging and recovery.

Install it via Composer:

php
composer require woocommerce/action-scheduler

Or bundle it in your plugin. Then schedule an action:

php
// Schedule a one-time action
as_schedule_single_action( time() + HOUR_IN_SECONDS, 'my_hourly_task', array( 'user_id' => 42 ) );

// Schedule a recurring action
as_schedule_recurring_action( time(), HOUR_IN_SECONDS, 'my_hourly_task', array( 'user_id' => 42 ) );

// Register the callback
add_action( 'my_hourly_task', function( $user_id ) {
    // Send email, sync data, generate report
    error_log( "Processing user $user_id" );
}, 10, 1 );

Action Scheduler stores actions in the database with a status field. On a busy site, you run it via WP-Cron or a server cron every few minutes, and it processes queued items in batches. The key advantages:

  • Automatic retries on failure with exponential backoff
  • Visible logs in the admin UI (via WooCommerce if installed)
  • Handles thousands of queued actions efficiently
  • Supports async processing without blocking page requests

Example: a SaaS app we built sends daily digest emails to 50,000 users. Using Action Scheduler, we queue all emails as actions, then process them in batches of 100 each minute. Failed sends are automatically retried, and the ops team can see exactly which emails succeeded or failed from the dashboard.

External schedulers hitting endpoints

For microservice architectures or highly available systems, you can decouple WordPress from scheduling entirely by using external schedulers like AWS EventBridge, Google Cloud Scheduler, or services like Zapier or Make (formerly Integromat).

This approach works like this: an external scheduler triggers an HTTP endpoint on your WordPress site at a set interval. Your site receives the request and processes the task. WordPress itself doesn't manage the schedule.

Example: set up AWS EventBridge to call your site every hour:

php
// In your plugin or theme's functions.php
add_action( 'rest_api_init', function() {
    register_rest_route( 'myapp/v1', '/sync-inventory', array(
        'methods'  => 'POST',
        'callback' => 'handle_inventory_sync',
        'permission_callback' => function( $request ) {
            // Verify request came from EventBridge
            $token = $request->get_header( 'Authorization' );
            return $token === 'Bearer ' . EXTERNAL_SCHEDULER_TOKEN;
        }
    ) );
} );

function handle_inventory_sync( $request ) {
    // Fetch from upstream, update WooCommerce products
    // Use Action Scheduler if you need to queue a lot of updates
    as_schedule_single_action( time(), 'sync_inventory_batch', array( 100 ) );
    return rest_ensure_response( array( 'status' => 'queued' ) );
}

This scales well because:

  • Your WordPress site doesn't manage cron; the external service does
  • You get monitoring and alerting from the external scheduler
  • If your site goes down, the scheduler keeps trying (you define retry logic)
  • Ideal for multi-instance deployments with load balancers

We use this pattern for a travel booking platform where inventory syncs happen every 15 minutes. AWS EventBridge calls a private endpoint that only it can reach, and we log every call. If a sync takes too long, we know immediately because EventBridge tracks execution time.

Monitor missed events

Whichever scheduling approach you choose, you need visibility into what's running and what's failing. A missed cron task is often silent—your emails don't send, your inventory doesn't update, and nobody notices until a customer complains.

Set up monitoring with these checks:

  1. Log every scheduled event. Before executing a task, write a log entry:
php
add_action( 'my_sync_task', function() {
    error_log( 'CRON: sync_task started at ' . current_time( 'mysql' ) );
    
    try {
        // Do work
        $result = perform_sync();
        error_log( 'CRON: sync_task completed. Result: ' . json_encode( $result ) );
    } catch ( Exception $e ) {
        error_log( 'CRON: sync_task FAILED. Error: ' . $e->getMessage() );
        wp_mail( 'ops@example.com', 'Cron failure', $e->getMessage() );
    }
}, 10, 0 );
  1. Store last-run timestamps. Use options to track when tasks last executed:
php
add_action( 'my_sync_task', function() {
    update_option( 'last_sync_task_run', current_time( 'mysql' ) );
    // ... do work
} );

Then create a health-check endpoint that alerts if a task hasn't run in the expected window:

php
add_action( 'rest_api_init', function() {
    register_rest_route( 'myapp/v1', '/health', array(
        'methods'  => 'GET',
        'callback' => function() {
            $last_sync = get_option( 'last_sync_task_run' );
            $hours_ago = ( strtotime( 'now' ) - strtotime( $last_sync ) ) / 3600;
            
            if ( $hours_ago > 1 ) {
                return new WP_Error( 'sync_stale', 'Last sync was ' . $hours_ago . ' hours ago', array( 'status' => 500 ) );
            }
            return rest_ensure_response( array( 'status' => 'healthy' ) );
        }
    ) );
} );
  1. Use an external monitoring service. Tools like Sentry, New Relic, or Datadog can track cron execution across your infrastructure. Log every event there, not just WordPress logs.

We learned this lesson the hard way: a site's cron task silently failed for three days because the server ran out of disk space. Our logs weren't being rotated. Now we use a third-party service that alerts if any cron task hasn't completed in 24 hours.

Common mistakes

  • Relying on WP-Cron alone on high-traffic sites. It's slow and unreliable because it depends on visitor activity. Disable it and use server cron.
  • Not setting a timeout on external cron requests. If your task hangs, your external scheduler will time out. Use set_time_limit() and break long tasks into batches.
  • Scheduling too many tasks at once. If you're queueing 100,000 actions, process them in small batches. Action Scheduler or external schedulers handle this, but don't try to process everything in one request.
  • Forgetting to monitor. "Set it and forget it" doesn't work. Always have a health check and alerting in place.
  • Using timestamps without timezone awareness. WordPress uses UTC internally; make sure your cron logic accounts for your site's timezone if you're scheduling by date.

Key takeaways

  • Disable WP-Cron on production sites and replace it with a real server cron job hitting wp-cron.php every 5 minutes.
  • Use Action Scheduler for complex queuing, retries, and visibility into task execution.
  • External schedulers (AWS EventBridge, Google Cloud Scheduler) are ideal for distributed systems or when you want to decouple scheduling from WordPress.
  • Always monitor scheduled tasks: log execution, store timestamps, and alert if tasks miss their window.
  • Test your cron setup before going live. Simulate low-traffic periods to verify tasks still run.
---
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles