Back to tutorials
Tutorial

WordPress Cron Troubleshooting Tutorial (2026): Fix Missed Scheduled Posts, WooCommerce Emails, and Slow WP-Cron

WordPress cron troubleshooting tutorial (2026) to fix missed schedules, stuck WooCommerce actions, and optimize WP-Cron with real cron.

By Anurag Singh
Updated on Sep 09, 2026
Category: Tutorial
Share article
WordPress Cron Troubleshooting Tutorial (2026): Fix Missed Scheduled Posts, WooCommerce Emails, and Slow WP-Cron

Scheduled posts that never publish. WooCommerce emails that show up hours late. Backups that only run after someone visits the site. Most of the time, that trail leads to WP-Cron.

On shared hosting and many VPS setups, WordPress “cron” runs on page loads instead of a real system scheduler. That makes it easy for jobs to slip.

This WordPress cron troubleshooting tutorial helps you find what’s failing, fix it without breaking checkout flows, and (when appropriate) replace WP-Cron with a predictable server cron job.

The steps apply to WordPress on VPS (Nginx/Apache), dedicated servers, and most cPanel-based shared hosting.

If you’re on a VPS and you want consistent background processing (orders, emails, cache warmers, security scans), you need control over cron, PHP, and how work gets queued.

A HostMyCode VPS makes WP-Cron fixes much simpler because you can schedule real cron and tune PHP-FPM without host-imposed limits.

What WP-Cron actually does (and why it fails on real hosting)

WP-Cron is a pseudo-cron. WordPress stores scheduled tasks in the database option named cron. It checks what’s due and tries to fire jobs when a request hits your site.

If nothing triggers that request—or loopback calls are blocked—your queue just sits there.

  • Low traffic sites: tasks run late because there aren’t enough page loads.
  • Busy sites: tasks can trigger too often, spiking CPU and clogging PHP workers.
  • Locked-down servers: loopback HTTP requests to wp-cron.php get blocked by firewall rules, security plugins, or WAF settings.
  • WooCommerce: Action Scheduler jobs pile up, delaying stock updates, webhooks, subscriptions, and transactional emails.

Your goal is predictable execution. Either make sure WP-Cron can reliably trigger, or disable the traffic-based trigger and run cron from the server on a schedule.

Quick symptom map: match what you see to the right fix

  • Missed scheduled posts → WP-Cron isn’t running, or the site has long low-traffic gaps.
  • WooCommerce orders “processing” forever / delayed emails → Action Scheduler backlog, or cron is timing out.
  • High CPU every few minutes → WP-Cron is spawning too often (usually heavy traffic + lots of plugins).
  • Backups/security scans only run sometimes → jobs depend on frontend traffic; move to system cron.
  • Site is fast, but admin is sluggish → cron runs during admin requests and steals PHP workers.

Step 1 — Confirm WP-Cron is enabled (and not silently disabled)

Open wp-config.php (usually in your WordPress document root).

On a typical VPS: /var/www/example.com/public_html/wp-config.php. On cPanel: /home/USER/public_html/wp-config.php.

Look for this constant:

define('DISABLE_WP_CRON', true);

If it’s set to true and you don’t have a real server cron configured, scheduled tasks won’t run. Remove it, or leave it in place and add a real cron in the steps below.

Also scan your plugin list for caching/security tools that “manage cron.” They can help. But migrations sometimes leave you with cron disabled and no replacement trigger.

Step 2 — See whether due cron events are piling up

WP-CLI is the quickest way to confirm what’s stuck. If you don’t have it, install it on a VPS (recommended) or use whatever tooling your host provides.

cd /var/www/example.com/public_html
wp --info

List cron events due now:

wp cron event list --due-now --fields=hook,next_run,recurrence --format=table

If you see a long list that never clears, cron isn’t executing. Or it’s failing partway through.

To test a single run safely (executes due events once):

wp cron event run --due-now

If the command hangs or throws errors, that’s your next lead. Save the output. It usually points to the plugin or resource limit breaking the run.

Step 3 — Check loopback requests (the most common hidden failure)

WordPress often triggers cron via an HTTP loopback request to itself. If loopback is broken, WP-Cron may never fire even though “everything looks fine” in the UI.

From the server, run:

curl -I https://example.com/wp-cron.php?doing_wp_cron=1

You want 200 or 302. Common bad outcomes:

  • 401/403: blocked by security rules, Basic Auth, or WAF.
  • 404: wrong WordPress path, bad rewrite rules, or wrong docroot.
  • 500: PHP fatal error or memory limit.
  • Timeout: DNS misroute, IPv6 issues, local firewall blocking loopback, or PHP workers are saturated.

If your site is behind Basic Auth (common on staging), WP-Cron will fail. In that case, use a tokenized endpoint or trigger cron server-side with wp cron via WP-CLI.

Step 4 — Fix “stuck” WooCommerce Action Scheduler jobs

WooCommerce leans heavily on Action Scheduler. When it backs up, you’ll see delayed emails, webhooks, and subscription renewals.

On the server (WP-CLI), check the queue size:

wp action-scheduler list --status=pending --per-page=20

If you’re staring at hundreds or thousands of pending jobs, don’t firehose the queue during peak traffic. Clear it in controlled batches.

Run a small batch:

wp action-scheduler run --batch-size=25

Repeat a few times and watch server load:

uptime
free -m
ps -eo pid,cmd,%cpu,%mem --sort=-%cpu | head

If one hook keeps failing, cron probably isn’t the real problem. It’s often a plugin integration issue or your SMTP/mail path.

For email-specific failures on cPanel/WHM, use the dedicated guide: cPanel email troubleshooting steps for SMTP/IMAP and webmail.

Step 5 — Convert WP-Cron to a real server cron (recommended on VPS/dedicated)

On production sites, a real system cron is easier to reason about. You stop relying on traffic. You run scheduled work on an actual timetable.

  1. Disable WP-Cron’s built-in trigger in wp-config.php:

    define('DISABLE_WP_CRON', true);
    
  2. Create a server cron that triggers WordPress cron every 5 minutes.

    Option A (preferred): WP-CLI method — avoids HTTP/WAF issues.

    */5 * * * * cd /var/www/example.com/public_html && /usr/local/bin/wp cron event run --due-now --quiet
    

    Common WP-CLI paths: /usr/local/bin/wp or /usr/bin/wp. Verify with which wp.

    Option B: HTTP method — useful on shared hosting without WP-CLI.

    */5 * * * * curl -sS -m 30 https://example.com/wp-cron.php?doing_wp_cron=1 >/dev/null
    

    Use -m 30 so cron doesn’t stack up if PHP is slow.

On cPanel, add this under Cron Jobs. On a VPS, use crontab -e for the correct user.

If PHP runs as a different user, match ownership to avoid permission problems.

If you manage multiple WordPress sites for clients, this is a practical reason to choose managed VPS hosting. You get predictable server-level scheduling and someone to sanity-check permissions, PHP-FPM pools, and load spikes.

Step 6 — Add a lock to prevent overlapping cron runs

Overlaps quietly drag performance down. If one run takes longer than 5 minutes, the next one starts anyway.

That stacks PHP processes and makes wp-admin feel heavy.

On Linux, use flock:

*/5 * * * * flock -n /tmp/wpcron-example.lock -c 'cd /var/www/example.com/public_html && /usr/local/bin/wp cron event run --due-now --quiet'

The -n flag means “don’t wait.” If a run is already in progress, this one skips.

Step 7 — Fix time drift and timezone mismatch (surprisingly common)

If your server clock drifts, scheduled posts miss their window. On VPS and dedicated servers, confirm NTP sync.

timedatectl status

You want System clock synchronized: yes. If not, on Ubuntu/Debian:

sudo apt update
sudo apt install -y systemd-timesyncd
sudo timedatectl set-ntp true

In WordPress, check Settings → General → Timezone. Use a city timezone (for example, Asia/Kolkata) instead of a raw UTC offset, especially if you observe DST.

Step 8 — Diagnose slow WP-Cron runs (PHP limits, memory, and long tasks)

If cron runs but feels slow, you’re usually short on PHP workers. Or one job is far too heavy to run every few minutes.

  • Check PHP memory limit: cron fatals often show as “Allowed memory size exhausted.”
  • Check max execution time: long-running tasks may get cut off mid-job.
  • Check PHP-FPM pool saturation: cron competes directly with frontend requests.

On a VPS with PHP-FPM, inspect the pool config (commonly /etc/php/8.3/fpm/pool.d/www.conf on Ubuntu).

Pay attention to:

pm.max_children
pm.max_requests
request_terminate_timeout

If you’re on Apache with mod_php (less common on newer stacks), moving to PHP-FPM usually gives you cleaner isolation and better queue control.

Also look for plugin jobs that don’t belong in frequent cron. Typical offenders include image regeneration every run, oversized sitemap rebuilds too often, or “scan the entire filesystem” tasks every 5 minutes.

Reduce frequency or move them to a nightly schedule.

Step 9 — Confirm cron is running (with logs you can actually use)

Cron failures are easy to miss because they often fail quietly. Make the run observable so you can spot problems before customers do.

Option A: log output from your cron line

*/5 * * * * flock -n /tmp/wpcron-example.lock -c 'cd /var/www/example.com/public_html && /usr/local/bin/wp cron event run --due-now' >> /var/log/wpcron-example.log 2>&1

Then review:

tail -n 50 /var/log/wpcron-example.log

Option B: external uptime/cron check (useful if you manage many sites). Pair cron conversion with monitoring so you notice failures quickly.

For a practical monitoring layout, use: uptime monitoring with external checks and on-server health endpoints.

Step 10 — Don’t break WordPress during migrations: cron, DNS, and SSL gotchas

WP-Cron issues often start right after moving from shared hosting to a VPS. These are the usual culprits:

  • Wrong site URL: cron hits the old domain or an http/https mismatch.
  • Mixed SSL: cron redirects in a loop because proxy headers aren’t set right.
  • DNS still resolving to old host: your “test” requests are hitting the wrong server.

If you’re planning a move, treat cron as part of the cutover.

This guide pairs well with: a DNS cutover checklist for zero-downtime migrations and the broader step-by-step: move a website from shared hosting to a VPS with rollback.

After cron is stable, make sure SSL renewals won’t become the next surprise.

If you use Let’s Encrypt, keep renewals simple and automated; see: SSL renewal troubleshooting for VPS and cPanel.

Practical checklist: a stable WP-Cron setup on VPS/shared hosting

  • DISABLE_WP_CRON is set to true only if you added a real cron job.
  • System cron runs every 5 minutes with a timeout and (ideally) a flock lock.
  • curl -I https://domain/wp-cron.php returns 200 or 302 (no infinite redirects).
  • NTP time sync is enabled (timedatectl shows synchronized).
  • WooCommerce Action Scheduler queue is not growing without bound.
  • You have a basic log or monitoring signal for cron failures.

If WP-Cron issues are costing you orders or support time, put the site on hosting where you control scheduling, PHP workers, and logging. A HostMyCode VPS is a clean fit for WordPress cron and WooCommerce background jobs, and managed VPS hosting covers the day-to-day tuning and upkeep.

FAQ

How often should I run the real cron for WordPress?

Every 5 minutes works for most sites. For busy WooCommerce stores, 1 minute can help, but only if your server has headroom and you use locking to prevent overlap.

Is it safe to disable WP-Cron on shared hosting?

Yes, as long as you add a cron job in cPanel (or your host’s scheduler). If you can’t add cron jobs, leave WP-Cron enabled and focus on fixing loopback access.

Why do scheduled posts miss publish time even though cron runs?

Check server time sync and WordPress timezone settings first. Then look for a plugin adding heavy cron jobs that delay the queue.

My cron job runs, but WooCommerce emails are still delayed. What next?

Inspect Action Scheduler failures and your mail path. If you’re sending from a cPanel server, start with mail server troubleshooting in WHM, and confirm SPF/DKIM/rDNS if deliverability is inconsistent.

Should I use curl or WP-CLI for the cron trigger?

On a VPS or dedicated server, WP-CLI is usually more reliable because it avoids HTTP blocks and redirect loops. On shared hosting without WP-CLI, curl is the practical choice.

Summary: the reliable fix is boring (and that’s good)

WP-Cron usually isn’t “broken.” It’s traffic-driven, which makes it unpredictable.

Once you disable the built-in trigger, run cron via WP-CLI (or curl) on a schedule, and add a lock to prevent overlap, missed schedules and WooCommerce job backlogs typically stop.

If you want it to stay stable as the store grows, run WordPress on infrastructure where you control cron, PHP-FPM, and monitoring.

Start with a HostMyCode VPS, and keep maintenance manageable with managed VPS hosting.