Got a call from a client running a busy WooCommerce store. Their inventory sync with a supplier’s API, which was supposed to run every 15 minutes, was completely out of whack. Sometimes it ran, sometimes it didn’t. Worse, their daily sales reports weren’t being generated reliably. The whole thing was a mess, and it was costing them money in oversells and bad data. The cause? Relying on a WordPress cron job for a business-critical task.
One thing to be clear about: WP-Cron is not a real cron. It isn’t a daemon or a service that runs continuously on your server. It’s a fake cron that piggybacks on site traffic. A scheduled task only gets a chance to run when someone visits the site, whether that’s a customer, a crawler, or you. On a high-traffic site it might feel reliable. But for critical tasks, especially ones scheduled during quiet hours, it’s a gamble.
The obvious fix that isn’t one
My first move was to bypass the traffic dependency. I SSH’d into the server and set up a proper system cron job to hit the wp-cron.php file every five minutes. It’s the go-to answer you’ll find on a dozen forums, and yes, it forced the WordPress cron schedule to run like clockwork. But it felt off. We were still pushing everything through the same old, fragile WP-Cron queue. The trigger was reliable now, but the underlying system was still the problem. It was a patch, not a real fix.
The problem is that when you lean on WP-Cron, your critical tasks are tied to the WordPress request lifecycle. If one task hangs, it can jam the whole queue. While thinking this through, I remembered an old post by Carl Alexander about his first open-source project, Chronos, which came out of a similar need for a better scheduler. It backed up what I already suspected: for serious work, you have to get the task out of the main application flow.
Decouple your tasks for real reliability
The fix is to decouple task execution from WordPress itself. Stop using wp_schedule_event for anything that matters. Instead, build a small PHP script you can run directly from the command line. It does one job and does it well.
<?php
// file: /path/to/your/wp-content/cli/sync-inventory.php
// Bootstrap WordPress to get access to its functions
require_once( __DIR__ . '/../../wp-load.php' );
function do_inventory_sync() {
// Your actual sync logic here.
// Connect to the API, get the data, update products.
$api_data = wp_remote_get('https://supplier-api.com/inventory');
if ( is_wp_error( $api_data ) ) {
error_log( 'Inventory sync failed: ' . $api_data->get_error_message() );
return;
}
$inventory = json_decode( wp_remote_retrieve_body( $api_data ), true );
// Process the inventory...
// update_post_meta( $product_id, '_stock', $new_stock_level );
error_log('Inventory sync completed successfully.');
}
// Run the function
do_inventory_sync();
exit(0);
Once you have that script, you set up a real server cron job to run it directly. No more hitting a URL: you run it straight from the shell.
*/15 * * * * /usr/bin/php /path/to/your/wp-content/cli/sync-inventory.php >/dev/null 2>&1
So what’s the point?
WP-Cron is fine for non-essential maintenance, like checking for theme updates or clearing out old transient data. But the moment a task touches money, data integrity, or anything business-critical, you need to use the right tools for it.
- Reliability: server cron jobs run on schedule, period. They don’t care whether your site has zero visitors or a million.
- Isolation: the task runs in its own process. It isn’t competing for resources with a visitor trying to load your homepage.
- Control: you control the execution environment directly and can handle logging and errors far more cleanly. For heavy tasks, you could even run them on something like AWS Lambda.
This stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
Stop treating WP-Cron like a reliable scheduler. It isn’t one. Use the right tool for the job and save yourself, and your clients, a big headache.