Bali, Indonesia
Insights

How to Verify Your WordPress System Cron Job Is Actually Running

October 20, 2026
0:00
You publish a scheduled post, set up an automatic database backup, or wait for an e-commerce subscription to renew, but the scheduled time passes and nothing happens. In the WordPress admin dashboard, the post status reads Missed schedule, or scheduled background tasks sit untouched for days. You added the standard line to disable default cron behavior and configured a crontab entry on your server weeks ago, yet the site behaves as if background processing has completely stopped. This silent failure is one of the most common issues support engineers encounter when site owners switch from standard web-driven cron to a dedicated Linux system cron. The web interface will not show an error banner when a system cron fails because the failure happens outside the web server's request cycle. To solve it, you need to verify three distinct layers: whether the operating system daemon is actually triggering the job, whether the command executes without error, and whether WordPress registers the execution and clears its internal queue.
By default, WordPress does not rely on the Linux system cron daemon (crond). It uses a virtual scheduler known as WP-Cron. Every time a visitor opens a page on your site, WordPress checks whether any scheduled tasks—such as publishing posts, checking for theme updates, or sending queued notification emails—are due. If tasks are pending, WordPress spawns an asynchronous HTTP request back to wp-cron.php to run them in the background. On sites with steady traffic, this works reasonably well, although it adds latency to normal page visits. On low-traffic sites, scheduled tasks run late because nobody visits to trigger them. On high-traffic or heavily cached sites, edge caching often strips incoming requests before PHP executes, causing tasks to stall entirely. To fix this, administrators define DISABLE_WP_CRON as true in wp-config.php and set up an entry in the system crontab, expecting Linux to execute tasks on a predictable clock. When this transition fails, the problem usually stems from a fundamental misunderstanding: the operating system cron daemon spawning a process does not guarantee that WordPress successfully ran its internal jobs. A shell command can trigger every ten minutes according to system logs, but if the command encounters a silent PHP fatal error, hits a timeout, or gets blocked by a security firewall, the internal queue remains frozen.
Your first diagnostic step is checking whether the Linux cron service is awake and executing the crontab entry you defined. You do not need to guess; the operating system logs every spawned cron execution. Connect to your server via SSH and examine the system log. The log file location depends on your Linux distribution. Debian and Ubuntu systems log cron activities to /var/log/syslog or systemd journal units, whereas RHEL, Rocky Linux, AlmaLinux, and CentOS write directly to /var/log/cron. Run a read-only search in your system logs to see if cron even attempts to run the task for your web user:
BASH
# For Debian/Ubuntu systems using journalctl
journalctl -u cron --since '1 hour ago' | grep -i 'wp-cron'

# For RHEL/Rocky Linux or standard log files
grep -i 'wp-cron' /var/log/cron | tail -n 20
If these commands return empty results, the system daemon is not executing your job. The most frequent causes are straightforward: the cron service itself is stopped, the crontab entry was placed under a disabled user account, or the crontab syntax contains an invalid timing parameter. If the logs show recurring lines with CRON[...] (username) CMD (...) matching your expected interval (such as every 10 or 15 minutes), the operating system scheduler is doing its part. However, a clean entry in the system cron log only confirms that the shell spawned the command. It does not tell you if that command exited with an error code, because standard crontab configurations silently discard standard output and standard error unless instructed otherwise.
There are two primary ways administrators set up system cron for WordPress: through an HTTP request using curl or wget, or directly through the PHP command-line interface (php /path/to/wp-cron.php or WP-CLI). Both methods can fail silently in different ways. If your crontab uses curl or wget to request https://example.com/wp-cron.php, the request behaves like an external visitor. Run that exact command manually from your server shell without silent flags (such as removing -s or -q) and inspect the output. Often, you will immediately see an HTTP 403 Forbidden, 401 Unauthorized, or 429 Too Many Requests response. External HTTP calls to wp-cron.php often fail because security plugins, Cloudflare rules, or web server configurations block requests without standard browser user-agents. Additionally, if your server cannot resolve its own domain name due to local DNS resolver misconfigurations, curl will hang until it hits a network timeout. When managing your own virtual server, issues like these frequently intersect with resource limits; as discussed in our look at managed WordPress hosting vs unmanaged VPS maintenance, self-managed infrastructure requires tracking both server-level networking and application behavior yourself. If your crontab invokes PHP directly using the binary path, test it by running the command in your shell as the web user (for example, www-data, nginx, or your specific cPanel/control panel user account):
BASH
sudo -u www-data php -q /var/www/html/wp-cron.php
Running this command manually exposes output that crontab hides. If your site has a plugin conflict that throws a fatal error, or if your CLI environment uses a different php.ini file with a lower memory limit than your web server, the error message will print directly to your terminal. A common discrepancy is memory allocation: CLI PHP frequently defaults to different limits than PHP-FPM, causing memory-intensive tasks like image generation or automated backups to die instantly without leaving an entry in your web server access logs.
Once you know the invocation command can run without crashing, you must verify what WordPress is actually doing with its task queue. WordPress stores scheduled events in the database inside the wp_options table under the option name cron. Interpreting raw serialized arrays from the database is cumbersome and error-prone. The cleanest tool for inspecting this queue is WP-CLI, the official command-line interface for WordPress. Navigate to your site directory and inspect the scheduled hooks:
BASH
wp cron event list --path=/var/www/html
This command outputs a clean list showing each registered hook, the arguments it accepts, the next scheduled execution time, and whether the task is recurring. Pay attention to the column indicating execution timing. If you see tasks marked with negative intervals or labeled as past due (for instance, showing that an event was supposed to run two hours or three days ago), your system cron is not clearing the queue. When tasks fall behind, it usually points to a transient mutex lock. When WordPress begins executing cron tasks, it sets a transient record in the database named doing_wp_cron containing a timestamp. This lock prevents concurrent cron jobs from running simultaneously and duplicating actions like charging payments or sending emails. If a scheduled task encounters a fatal error, an unhandled exception, or a hard server process termination midway through execution, the script terminates before removing the doing_wp_cron lock. Subsequent cron invocations see the lock, assume another process is still actively working through the queue, and exit immediately without processing any hooks. The queue remains stuck until the lock times out, which by default takes several minutes, but can loop continuously if the same broken task crashes on every attempt. You can pinpoint the broken task by running the due events individually through WP-CLI:
BASH
wp cron event run --due-now --path=/var/www/html
WP-CLI will execute each pending hook sequentially and output the status. If a specific plugin task halts execution due to a database error, missing file dependency, or external API timeout, WP-CLI prints the stack trace directly to your terminal. This immediately identifies which plugin is responsible for halting the entire background queue.
Once you have resolved the underlying fatal error or permission barrier, replace fragile HTTP-based cron commands with a direct WP-CLI invocation in your crontab. Executing wp cron event run --due-now via system cron bypasses web server timeouts, avoids external HTTP firewalls entirely, and provides cleaner logging capabilities. Verifying that scheduled tasks work properly is not a one-time check when launching a site. Any plugin update or change to server-side PHP limits can silently disrupt background execution. By checking the system logs first, running the invocation manually in the terminal second, and validating the internal event table with WP-CLI third, you can systematically diagnose any broken cron pipeline without relying on guesswork.