- WP-Cron is not a real scheduler. It only fires when someone visits the site, so a quiet site runs nothing.
- Full-page caching makes it worse: cached visits never reach PHP, so nothing triggers the queue.
- The fix is two lines: disable WP-Cron in wp-config.php and add a server cron job calling wp-cron.php every five minutes.
- On WooCommerce, a stalled queue means unsent order emails, stock that never releases, and subscriptions that do not renew.
- Check the queue with
wp cron event list --due-now. Anything overdue by hours confirms the diagnosis immediately.
Why does WordPress miss scheduled tasks?
Because WP-Cron is not a scheduler. It is a list of jobs that WordPress checks on page load, and it only runs when someone visits the site. No visitors means no checks, and no checks means nothing runs. A task scheduled for 3am on a quiet site may not fire until the first visitor arrives hours later.
| WP-Cron | Server cron | |
|---|---|---|
| Triggered by | A page load | The operating system clock |
| Runs on a site with no traffic | No | Yes |
| Runs when pages are cached | No | Yes |
| Accuracy | Whenever someone happens to visit | To the minute |
| Cost per run | Delays the visitor who triggered it | A separate background process |
Every row is a reason to move to server cron. There is no case where WP-Cron is the better option on a production site.
Caching turns a small problem into a total one
Once full-page caching is in front of your site, most visits are served from the cache and never reach PHP at all. WP-Cron therefore never gets called. Sites often see scheduled tasks stop the same week caching was switched on, and the two are rarely connected at the time.
How do I check whether WP-Cron is working?
WP-CLI answers this in one command. Anything sitting in --due-now that should have run hours ago confirms the queue is stalled rather than merely busy.
cd /var/www/example.com
# Everything scheduled, with next run times
wp cron event list
# Only what is overdue right now
wp cron event list --due-now
# Is WP-Cron even reachable?
wp cron test
# Run the whole queue by hand and see what happens
wp cron event run --due-now
# Registered schedules (hourly, twicedaily, daily, plus plugin ones)
wp cron schedule listwp cron test checks that the site can call its own wp-cron.php, which fails on servers that cannot resolve their own hostname.# Does the endpoint respond at all?
curl -sI https://example.com/wp-cron.php?doing_wp_cron
# Is it disabled in wp-config.php?
grep -i 'DISABLE_WP_CRON\|ALTERNATE_WP_CRON' wp-config.php
# Read the queue straight from the database
wp db query "SELECT option_value FROM wp_options WHERE option_name='cron'" \
| head -c 2000| Symptom | Almost certainly |
|---|---|
| Scheduled posts stay in "Missed schedule" | The queue is not running at all |
| Order emails arrive hours late, or in bursts | Only running when a visitor happens to arrive |
| Backup plugin skipped last night | No overnight traffic to trigger it |
| Stock stays reserved after an abandoned checkout | WooCommerce cleanup tasks are not firing |
| Everything runs, but the site feels slow at random | WP-Cron is running inline and delaying visitors |
The last row is the other failure mode: WP-Cron working, but making a visitor wait for it.
How do I replace WP-Cron with a server cron job?
Two steps. Disable the traffic-triggered behaviour in wp-config.php, then have the operating system call the same endpoint on a fixed schedule. Five minutes is the right interval for almost every site.
// Add above the line that says "That's all, stop editing!"
define('DISABLE_WP_CRON', true);# Edit the crontab for the user that owns the site
sudo crontab -u www-data -e
# Preferred: WP-CLI, no HTTP request involved
*/5 * * * * cd /var/www/example.com && /usr/local/bin/wp cron event run --due-now --quiet
# Alternative: call the endpoint over HTTP
*/5 * * * * curl -sS -o /dev/null https://example.com/wp-cron.php?doing_wp_cron
# Confirm it is installed
sudo crontab -u www-data -lUse the site's own user, not root
Running WP-CLI as root creates files owned by root inside your web root, which the web server then cannot write to. WP-CLI will warn you and refuse by default. Always use crontab -u www-data -e, or whichever user owns the site. See Linux file permissions for web servers.
# Is cron executing the job?
grep CRON /var/log/syslog | tail -20 # Debian / Ubuntu
sudo journalctl -u crond --since '1 hour ago' # RHEL family
# Should be empty a few minutes after each run
wp cron event list --due-now
# Log the output so you can see failures
*/5 * * * * cd /var/www/example.com && /usr/local/bin/wp cron event run --due-now \
>> /var/log/wp-cron.log 2>&1What breaks on WooCommerce when cron stalls?
More than most people realise. WooCommerce runs a great deal through the scheduler, and it also runs Action Scheduler on top of it, which handles the bulk of the background work. When the queue stalls, the shop keeps taking orders while quietly failing to complete them.
| Task | What customers see when it stalls |
|---|---|
| Order confirmation emails | No confirmation, followed by a support ticket |
| Stock release after a failed payment | Products showing out of stock that are not |
| Subscription renewals | Renewals never charged, revenue quietly lost |
| Abandoned cart cleanup | The database grows and admin pages slow down |
| Product feed and stock sync | Marketplace listings drift out of date |
| Scheduled sale prices | A sale that starts or ends on the wrong day |
Rows one and three are the expensive ones, and both fail silently.
# Overall queue health
wp action-scheduler status
# Anything stuck as pending long past its date
wp action-scheduler list --status=pending --per_page=20
# Run the queue by hand
wp action-scheduler run
# Clear out completed and failed records: this table gets very large
wp action-scheduler clean --batch-size=1000
# How big has it grown?
wp db query "SELECT COUNT(*) AS actions FROM wp_actionscheduler_actions"Action Scheduler is visible in the admin
WooCommerce, Status, Scheduled Actions shows the queue with statuses and timestamps, without any command line. If Pending has thousands of entries with dates in the past, cron has not been running and you have your answer.
If the queue is running but slowly, the constraint is usually database or CPU rather than the schedule. Troubleshooting high CPU and memory on WordPress covers finding out which, and safely trimming a large WooCommerce database covers the table growth that causes it.
How often should the cron job run, and what if it overlaps?
Every five minutes suits almost every site. Every minute is worth it for a busy shop where order emails should be near-instant. Anything less frequent than fifteen minutes starts to feel broken to customers waiting on a confirmation.
| Interval | Suits | Trade-off |
|---|---|---|
| */1 * * * * | Busy shops, near-instant emails | More background load, rarely a problem |
| */5 * * * * | Almost every site | The sensible default |
| */15 * * * * | Low-traffic brochure sites | Customers may notice the delay on a form |
| 0 * * * * | Nothing time-sensitive at all | Too slow for anything transactional |
Start at five minutes. Move to one only if you can point at something that needs it.
# flock stops a second run starting while the first is still going
*/5 * * * * /usr/bin/flock -n /tmp/wpcron-example.lock \
/bin/sh -c 'cd /var/www/example.com && /usr/local/bin/wp cron event run --due-now --quiet'-n means fail immediately rather than queue. Without this, a long-running job on a one-minute schedule can pile up and exhaust your PHP workers.Overlapping cron runs can take a site down
If one run takes eight minutes and the job fires every five, you accumulate concurrent PHP processes until the pool is exhausted and the site returns 502. Always use flock on anything scheduled more often than it reliably completes.
This is one of the things a managed platform should handle for you. On G7Cloud, WordPress scheduled tasks run from a system cron on the site's own container rather than depending on visitor traffic, so a quiet site and a heavily cached site both keep their queues moving. See how our WordPress hosting works.
Frequently asked questions
Why are my WordPress scheduled posts missing their schedule?
Because WP-Cron only fires when someone loads a page. A post scheduled for 3am on a site with no overnight visitors will not publish until the first visit afterwards. Full-page caching makes it worse, since cached visits never reach PHP. Replace WP-Cron with a server cron job.
Should I disable WP-Cron?
Yes, on any production site, but only as half of the change. Set define('DISABLE_WP_CRON', true); in wp-config.php and add a server cron job calling the queue every five minutes. Disabling it without adding the cron job stops every scheduled task on the site.
What is the difference between WP-Cron and Action Scheduler?
WP-Cron is WordPress's own scheduler, triggered by page loads. Action Scheduler is a queue library that WooCommerce and many plugins use for background work, and it is itself started by WP-Cron. If WP-Cron stalls, Action Scheduler stalls with it.
How often should the WordPress cron job run?
Every five minutes for most sites. Every minute for a busy shop where order confirmations should arrive immediately. Use flock at that frequency so a slow run cannot overlap with the next one and exhaust your PHP workers.
Is WP-CLI or curl better for the cron job?
WP-CLI. It runs PHP directly with no HTTP request, so page caching, firewall rules, bot protection and TLS problems cannot interfere. The curl form is a reasonable fallback on hosting that does not offer WP-CLI or shell access.
Why do my WooCommerce order emails arrive late?
Usually a stalled scheduler. WooCommerce queues most emails rather than sending them during checkout, so if nothing is processing the queue they sit there until a visitor triggers it. Check WooCommerce, Status, Scheduled Actions for pending items with dates in the past.
About G7Cloud Engineering
Articles written by the engineers who build and run G7Cloud: UK managed hosting and the AI Website Builder. We write about what we operate every day: containers, backups, databases, and the small-business websites that run on them.
More about G7Cloud →