
Missed scheduled posts, WooCommerce stock not updating, backups that never run, or a “Publishing failed” message that goes away after a refresh are classic cron problems. In 2026, most WordPress sites still depend on wp-cron.php, which only runs when something hits the site. That’s unreliable on low-traffic sites. It can also become a performance tax on busy ones. This WordPress cron troubleshooting tutorial shows you how to confirm what’s broken, prove it with logs, and switch to a real system cron that runs on your schedule.
This guide assumes you manage WordPress on a Linux VPS (Ubuntu/Debian/Rocky/AlmaLinux) or a dedicated server. If you don’t want to handle the OS side, managed VPS hosting from HostMyCode covers updates, monitoring, backups, and incident response. You can stay focused on the site.
What you’ll fix (and what “broken cron” looks like)
WordPress scheduled tasks run via WP-Cron, a pseudo-cron triggered by HTTP requests. When it fails, it usually fails in a few predictable ways:
- Missed schedule: posts stick at “Scheduled” and publish late (or never).
- WooCommerce delays: expired coupons, abandoned cart emails, and inventory sync jobs lag.
- Plugin jobs pile up: security scans, cache warmers, and backup plugins queue tasks but don’t execute.
- Slow requests: page loads randomly spike because WP-Cron runs heavy tasks during a normal web request.
- High CPU on busy sites: wp-cron triggers too often under traffic, causing unnecessary PHP work.
You’re aiming for two outcomes. Scheduled tasks should run on time. Cron work should stop stealing cycles from real page requests.
Pre-flight checks: confirm you’re seeing a cron issue
Before you change config, confirm the symptoms. Capture a baseline so you can verify the fix later.
- Check for missed schedules inside wp-admin
Install the lightweight plugin “WP Crontrol” (commonly used by admins) and open Tools → Cron Events. Look for events marked “missed schedule” or a backlog that keeps growing. - Check PHP timeouts
If cron starts but dies mid-run, you’ll usually see timeouts or fatal errors in your web/PHP logs. On Nginx + PHP-FPM, check:
# Common locations (Ubuntu/Debian)
sudo tail -n 200 /var/log/nginx/error.log
sudo tail -n 200 /var/log/php8.3-fpm.log
# Apache + PHP-FPM (or mod_php) varies by distro
sudo tail -n 200 /var/log/apache2/error.log
sudo tail -n 200 /var/log/httpd/error_log
If you’re not sure whether cron is the cause or a symptom, keep these logs open while you test. Also follow HostMyCode’s workflow for isolating slow responses: diagnose slow website response on Ubuntu.
- Confirm your server clock and timezone
Cron depends on system time. If your clock or timezone is off, schedules drift and “missed” events start showing up.
timedatectl
# If needed (example):
sudo timedatectl set-timezone Asia/Kolkata
Step 1 — Verify WP-Cron can run at all (HTTP access test)
WP-Cron runs by calling wp-cron.php over HTTP. If that endpoint is blocked, scheduled tasks won’t run.
From the server, request your own cron endpoint:
# Replace example.com with your domain
curl -I https://example.com/wp-cron.php?doing_wp_cron=1
Healthy results look like:
- 200 (or sometimes 204, depending on caching/proxy rules).
- No redirects to login, no 403/401, and no 5xx errors.
If you get a 403, it’s usually one of these:
- A WAF rule blocking
wp-cron.php - Nginx/Apache deny rules
- Basic auth applied site-wide (common on staging)
If Basic Auth is enabled, don’t depend on visitor-triggered cron. Either exempt wp-cron.php from auth, or switch to system cron (covered below).
Step 2 — Turn on WordPress cron logging (without flooding your disk)
WordPress doesn’t include clear cron logs out of the box. The fastest way to get useful signal is to enable debug logging temporarily, then reproduce the issue.
Edit wp-config.php (usually in the site root, e.g. /var/www/example.com/public_html/wp-config.php or similar):
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
Then tail the log while you trigger cron:
sudo tail -f /var/www/example.com/public_html/wp-content/debug.log
Run a cron event from WP Crontrol (or trigger wp-cron via curl). Watch for:
- Fatal errors from a plugin (very common)
- Memory exhausted
- “Allowed memory size” errors
- Database connection errors (often intermittent under resource pressure)
Important: once you’ve captured the error, revert WP_DEBUG. Otherwise, you’ll keep generating noisy production logs.
Step 3 — Check for “cron storms” and repeated triggers on busy sites
On high-traffic sites, WP-Cron can fire far more often than you expect. Each hit starts PHP, inspects the cron queue, and may run multiple jobs.
If you see random CPU spikes, measure how often wp-cron.php is requested.
Nginx access logs example:
sudo awk '$7 ~ /wp-cron\.php/ {count++} END {print count}' /var/log/nginx/access.log
Apache access logs example:
sudo awk '$7 ~ /wp-cron\.php/ {count++} END {print count}' /var/log/apache2/access.log
Hundreds or thousands of hits per hour is wasted CPU. A system cron fixes this by running exactly once per minute (or whatever interval you set). It won’t run more often just because traffic is high.
Step 4 — Replace WP-Cron with a real system cron (recommended)
This is the dependable approach on a VPS or dedicated server. You stop running cron during page loads. You run it from the OS on a predictable schedule.
4.1 Disable WP-Cron in WordPress
Edit wp-config.php and add:
define('DISABLE_WP_CRON', true);
This prevents WordPress from executing cron work during normal requests.
4.2 Add a system cron job
Pick one method:
- curl/wget: easy and available on almost every server
- WP-CLI: runs inside WordPress without HTTP, usually cleaner and more reliable
Option A: curl (simple and dependable)
sudo crontab -u www-data -e
Add this line (adjust domain):
* * * * * curl -fsS https://example.com/wp-cron.php?doing_wp_cron=1 >/dev/null 2>&1
Notes: On some distros, your web user is apache (RHEL family) instead of www-data. If the site sits behind Basic Auth, curl will need credentials. In that case, you should use WP-CLI.
Option B: WP-CLI (best for performance and auth edge cases)
From your WordPress directory, confirm WP-CLI works:
cd /var/www/example.com/public_html
sudo -u www-data wp --info
Then run cron manually:
sudo -u www-data wp cron event run --due-now
If that succeeds, schedule it:
sudo crontab -u www-data -e
* * * * * cd /var/www/example.com/public_html && wp cron event run --due-now --quiet
WP-CLI avoids external HTTP calls. It also keeps cron working even if you block wp-cron.php at the edge.
4.3 Validate the fix
- In WP Crontrol, confirm events no longer show “missed schedule”.
- Schedule a post 5 minutes ahead and confirm it publishes on time.
- Watch server load during normal traffic; it should be smoother.
Step 5 — Stop cron from getting killed: PHP-FPM timeouts, memory, and locked sessions
Sometimes cron triggers correctly, but long-running jobs get terminated. The fastest wins usually come from removing resource ceilings that don’t match real workloads.
5.1 Check PHP-FPM settings (common on VPS hosting)
Typical locations:
- Ubuntu/Debian:
/etc/php/8.3/fpm/php.iniand pool config in/etc/php/8.3/fpm/pool.d/www.conf - AlmaLinux/Rocky:
/etc/php.iniand/etc/php-fpm.d/www.conf
Settings to review:
memory_limit(start at 256M for typical sites; higher for heavy WooCommerce)max_execution_time(cron tasks often need more than 30 seconds)- PHP-FPM pool
request_terminate_timeoutif you use it
If you want a WordPress-friendly baseline, follow HostMyCode’s tuning approach here: PHP-FPM performance tuning. It’s not a cron guide. It does cover the same timeouts and worker limits that commonly break scheduled tasks.
5.2 Look for session locking
Some plugins trigger cron via admin-ajax while a user session is active. PHP session locks can serialize requests and stall cron work. Running cron via WP-CLI reduces that risk because it doesn’t depend on frontend sessions.
Step 6 — Prevent bot traffic from triggering wp-cron repeatedly
If you can’t disable WP-Cron, reduce abusive triggering. Aggressive crawlers can force constant cron checks. That burns PHP workers for no benefit.
Two safe moves:
- Disable WP-Cron and use system cron (best)
- Rate-limit the worst endpoints if you can see clear abuse patterns
If bots are hammering your login and admin endpoints, follow HostMyCode’s rate limiting workflow: Nginx rate limiting tutorial. Once junk traffic stops tying up PHP, cron often “mysteriously” becomes stable again.
Step 7 — Add a cron health check you can actually trust
Fixing cron is only half the job. You also want early warning when it breaks again.
7.1 Log cron execution (lightweight)
Instead of discarding output, log a timestamp per run. Example using WP-CLI:
sudo crontab -u www-data -e
* * * * * cd /var/www/example.com/public_html && (date -Is; wp cron event run --due-now --quiet) >> /var/log/wp-cron-run.log 2>&1
Then verify:
sudo tail -n 50 /var/log/wp-cron-run.log
7.2 Monitor the server so you catch regressions
Cron failures often show up indirectly: CPU spikes, PHP-FPM queues growing, memory pressure, or disk space dropping. Basic monitoring and alerts help you catch that before customers notice. This pairs well with: server monitoring setup guide with Netdata.
Step 8 — If you’re on cPanel/WHM: do cron safely without breaking permissions
On cPanel servers, each site typically runs under its own account user rather than a shared www-data user. The approach stays the same.
First, disable WP-Cron. Then run cron as the correct account user.
8.1 Identify the cPanel username
In WHM, search the domain and note the account username (example: client1). The WordPress path is often:
/home/client1/public_html
8.2 Add cron from cPanel
In the user’s cPanel, open Cron Jobs and schedule WP-CLI if installed, or use wget/curl. Example WP-CLI command:
* * * * * cd /home/client1/public_html && /usr/local/bin/wp cron event run --due-now --quiet
WP-CLI path varies. Check it with:
which wp
8.3 Avoid running cron as root
Running cron as root can create root-owned files in wp-content. That quickly turns into permission errors and cleanup work. Run cron as the site owner.
Step 9 — Quick checklist: common root causes and fast fixes
- Low traffic site: WP-Cron never triggers → disable WP-Cron; run system cron every minute.
- Basic auth / maintenance mode:
wp-cron.phpblocked → use WP-CLI cron. - 403 to wp-cron.php: WAF/rules deny → allow that endpoint (or switch to WP-CLI).
- High CPU: too many wp-cron hits → disable WP-Cron; system cron.
- Fatal plugin errors: debug.log shows stack traces → update/replace plugin; retest.
- Timeouts/memory: PHP limits too low → raise limits; tune PHP-FPM workers.
- Time drift: wrong timezone/clock → fix with
timedatectl.
Summary: the stable cron setup you should run in 2026
If you only change one thing, do this: disable visitor-triggered WP-Cron and run cron from the OS. Scheduled work becomes predictable. Page requests stop doing background labor.
Debugging also gets easier because you control the trigger.
If you’re building a WordPress business on a VPS, reliable scheduling is basic hosting hygiene. It belongs next to backups and monitoring.
HostMyCode can host WordPress on performance-tuned plans, or give you the control of a VPS. Start with HostMyCode VPS for full control, or choose WordPress hosting if you want fewer sysadmin chores.
If your WordPress cron jobs keep failing because the server is underpowered or painful to maintain, move the site to a stack built for consistent performance. HostMyCode offers HostMyCode VPS plans for hands-on admins and managed VPS hosting when you want OS maintenance, security updates, and monitoring handled for you.
FAQ
Should I set the system cron to run every minute?
For most WordPress sites, yes. WordPress schedules are minute-granular, and running every minute keeps timing accurate without relying on traffic. If a job is heavy, keep the interval and fix the job (or reschedule that workload off-peak).
Will disabling WP-Cron break scheduled posts?
Not if you add a real cron job. Disabling WP-Cron only turns off the visitor-triggered mechanism; the system cron becomes the trigger instead.
Is WP-CLI better than curl for cron?
Usually. WP-CLI avoids HTTP/WAF/auth issues and runs directly inside WordPress. curl is fine if wp-cron.php is reachable and you want the smallest possible setup.
How do I know which user should run the cron job?
Run it as the same user that owns the WordPress files and normally runs PHP for that site: www-data on many Nginx/Apache setups, or the cPanel account user on WHM/cPanel servers.
What else should I monitor after fixing cron?
Track CPU load, PHP-FPM active processes, memory pressure, and disk space. A cron fix won’t help if the server regularly runs out of RAM or storage.