How to disable wp-cron and use system cron for WP
WP-Cron only run when person visit your site, so e dey slow things down. Learn how to use WP-CLI for system cron to make your site fast and stop those pending task errors.
Wetin be wp-cron, and why system cron dey replace am
WP-Cron na the task scheduler wey dey inside WordPress, and e dey only run when person request page. Nothing inside WordPress dey wake up by imsef. For every request wey no come from cache, WordPress go read list of scheduled jobs, and if one dey due, e go fire second HTTP request back to itself for /wp-cron.php to do the work. If you move that job go system cron, you go get one predictable run for fixed schedule, whether the site get thousand visitors for that minute or zero.
Two lines dey do the real work: one constant for wp-config.php, and one crontab entry. Everything else for this guide na the part wey those two lines no tell you. Which user the job must run as, how to prove say the scheduled events really fire, and the three ways the setup fit fail without printing anything for the site.
The examples dey use /srv/www/example.com as the WordPress directory and www-data as the web server user. Change the paths and user to your own everywhere.
Which visitor dey trigger cron costs for busy site
Every request wey no get cache dey pay for the check. WordPress dey load the cron option, compare timestamps, and when time reach, e go call spawn_cron(), wey go send non-blocking loopback request go /wp-cron.php. The visitor no dey wait for the result. But one PHP worker dey wait. For small VPS wey dey run PHP-FPM with pm.max_children = 5, one slow scheduled job fit hold one-fifth of your PHP capacity for as long as e take, and e most likely go trigger during your busiest minute, because na that time plenty page loads dey happen.
WordPress dey limit duplicates. E dey take lock wey get lifetime of WP_CRON_LOCK_TIMEOUT, wey be 60 seconds by default, so simultaneous visitors no go each start one run. The lock dey cap duplication. E no dey comot the work from the request path.
Count how many times e dey fire for your own server before you decide whether e matter. Each loopback dey show for the web server access log:
sudo grep -c 'wp-cron.php' /var/log/nginx/access.logApache dey write go /var/log/apache2/access.log instead. If the count reach thousands per day, that one na real cost, and na the kind number you suppose measure for your own box instead of reading am for article, the same way you go benchmark a VPS before and after any other change.
Caching dey change the matter. If page cache dey serve most requests as static HTML, PHP no go ever run for those requests, so the cron check no go ever happen. Busy site wey get heavy cache dey start to behave like the quiet site wey dey below.
Wetin dey cause make cron no work for site wey no get traffic
If visitor no show, cron no go run. Site wey only get small traffic per day go run scheduled jobs only when person visit, and e go run for any random time wey that person show up.
All the symptoms dey look the same. Post wey you schedule for 09:00 go just stay for list as Missed schedule until person load page. Backup plugins go skip the night. Update checks go delay, so dashboard no go show say update dey even if security release don drop. Order emails, renewal notices, and expiry warnings go reach late.
None of this one dey show error for log. WordPress no see the job as late, because the job no even start at all.
Step 1: off di visitor trigger for inside wp-config.php
Open /srv/www/example.com/wp-config.php come add dis constant:
define( 'DISABLE_WP_CRON', true );Put am top of di line wey read /* That's all, stop editing! Happy publishing. */, sake of say di line wey dey just under dat comment need wp-settings.php, and wp-settings.php na where WordPress dey hook di cron check onto init. If you define constant after dat require, e go too late to change anything, and di file go look correct but di trigger go still dey run.
Confirm say di line dey where you think say e dey:
grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.phpDis constant no dey stop events from to dey scheduled. Plugins go still dey add jobs go di queue, exactly as e be before. E only dey stop page loads from to run dat queue, wey mean say di queue no go run at all until you finish step 3.
E no still dey block direct requests to /wp-cron.php. Anybody fit still request dat URL, and dat one usually no get wahala sake of say di file only dey run wetin suppose run. To block am for your web server config na optional. If you block am, di curl fallback wey dey near di end of dis guide go stop to dey work too.
Step 2: install WP-CLI
WP-CLI na di official command line tool for WordPress. E need di PHP command line binary, wey be separate package from di web server PHP module.
php -v
sudo apt install -y php-cliInstall WP-CLI from di phar build, as di official install guide recommend:
cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --infophp wp-cli.phar --info go print di PHP binary path, di PHP version, and di WP-CLI version. If e print all three, di phar dey work. As of August 2026, di install guide talk say PHP 7.2.24 na di minimum, and Ubuntu 24.04 dey come with PHP 8.3, so current server dey well pass dat level. Update am later wit sudo wp cli update.
Run WP-CLI as di site user, no ever run am as root:
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com core versionIf you try run am as root, WP-CLI no go start:
Error: YIKES! It looks like you're running this as root.E go suggest --allow-root. No use dat one for here. Di reason dey for di first failure mode wey dey below.
Also note say sudo -u www-data -i no dey work, because dat account login shell na /usr/sbin/nologin and you go get This account is currently not available.. If you pass di command straight to sudo -u, e go skip di login shell, so e go run well.
Now confirm say WordPress sef see di constant from step 1:
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'Dat one go print bool(true). If you see fatal error about undefined constant, e mean say di define() line no reach, wey usually mean say e land below di require.
Step 3: add the cron entry as the correct user
The correct user na the one wey get the files wey PHP dey write. Check both sides:
stat -c '%U %G' /srv/www/example.com/wp-content/uploads
grep -E '^(user|group) = ' /etc/php/8.3/fpm/pool.d/www.confFor default Ubuntu install, both of dem go be www-data. If you give the site im own PHP-FPM pool with im own user, wey be how per-site setup for a LAMP stack on Ubuntu 24.04 usually dey end, use that user for everything wey follow.
Make one log directory wey that user fit write:
sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cronEdit that user im crontab:
sudo crontab -u www-data -eAdd this one line:
*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1Make we break am down. */5 dey run am every five minutes. flock -n dey take lock file and e go stop immediately if one previous run still dey hold am. /usr/local/bin/wp na the absolute path, wey cron need. --path allow the command run from any working directory. --due-now dey run only the events wey their time don reach, instead of every event for the queue. The redirect dey send normal output and errors go one file wey you fit read.
That redirect no be optional for real life. Cron dey mail job output go the user, but most VPS images no get mail transfer agent installed, so cron go log (CRON) info (No MTA installed, discarding output) and throw the output away. One file go keep the evidence.
Check the file wey you save:
sudo crontab -u www-data -lFor plenty sites, use one line for each one with staggered minutes so dem no go all start at the same time:
*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-one.lock /usr/local/bin/wp --path=/srv/www/one.example.com cron event run --due-now >> /var/log/wp-cron/one.log 2>&1
2-59/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-two.lock /usr/local/bin/wp --path=/srv/www/two.example.com cron event run --due-now >> /var/log/wp-cron/two.log 2>&1The log go grow forever unless you rotate am. Write /etc/logrotate.d/wp-cron:
/var/log/wp-cron/*.log {
weekly
rotate 4
missingok
notifempty
compress
create 640 www-data www-data
}Check say e parse well without touch anything: sudo logrotate --debug /etc/logrotate.d/wp-cron.
Step 4: make sure say the scheduled events actually run
If you save one crontab line, e no mean say e go work. Start from the one wey easy to check reach the one wey go give you final answer.
First, cron start the command? Cron dey log go the journal under im own unit:
journalctl -u cron.service --since "15 min ago" | grep wpOne healthy entry go look like this, wey we don cut the timestamp and host name comot for front:
CRON[24913]: (www-data) CMD (/usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1)That line mean say cron don start your command as www-data. E no talk whether the command work or e fail.
Second, WordPress execute anything? Read the log file:
sudo tail -n 20 /var/log/wp-cron/example.logWP-CLI dey print one line per event, then e go show total:
Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.Errors dey land for the same file, and na why we use 2>&1 be that. Most times, nothing go dey to run, so the file go empty. Make you read the file after one run wey you know say work dey wait.
Third, prove say everything work from start to finish. Schedule one marker event and watch am disappear:
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event schedule wp_cli_cron_check now
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event list --fields=hook,next_run_relative --format=csv | grep wp_cli_cron_checkWait for one interval, then run the list command again. The hook go don comot, because once one-off event run, e go comot from the queue. No plugin dey register callback for that hook name, so running am no go do anything else for the site. If the hook still dey the list after two intervals, the queue no dey run, and the first two checks go tell you whether the problem na cron or WP-CLI.
No use wp cron test for this one. That command dey check whether visitor-triggered spawning dey work, and e go show error when DISABLE_WP_CRON be true. For server wey you configure well, that error na the expected output, e no mean say something spoil.
The systemd timer alternative
If all other scheduled work for the server dey run as systemd services and timers, put WordPress for there too. Every run go show for inside systemctl list-timers, and the output go enter the journal instead of one file wey you go need rotate.
Write /etc/systemd/system/wp-cron-example.service:
[Unit]
Description=Run due WordPress cron events for example.com
[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com cron event run --due-nowThen /etc/systemd/system/wp-cron-example.timer:
[Unit]
Description=Run WordPress cron for example.com every 5 minutes
[Timer]
OnCalendar=*:0/5
AccuracySec=30s
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers wp-cron-example.timer
journalctl -u wp-cron-example.service -n 20Systemd no go run two copies of the same service at the same time, so this version no need flock. Persistent=true make sure say e go run any task wey miss when the machine off, something wey crontab no fit do.
Choose either crontab or the timer. If you run both for the same site, the queue go dey drain twice, and your customers go see duplicate runs for email or order jobs.
Why the cron job no suppose run as root
Dis one na di first of di three ways wey di setup fit fail. If you put di job inside root crontab, WP-CLI go stop before e even start any work:
Error: YIKES! It looks like you're running this as root.Di queue no go ever run, and if you no redirect di output, you no go ever see di message. Di dangerous way to fix am na to add --allow-root, because if you do am, every file wey plugin write during dat run go belong to root. Di next web request go run as www-data, e no go fit write inside dose directories, and di site go start to show error like:
Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?Fix di ownership, den move di job:
sudo chown -R www-data:www-data /srv/www/example.com/wp-contentRoot crontab and www-data crontab na separate files, so if you delete di line from one, e no go affect di oda one. Check both:
sudo crontab -u root -l
sudo crontab -u www-data -lWhy cron dey report wp: not found
Dis na the second time wey dis error dey show. Cron dey give user jobs very short PATH, /usr/bin:/bin. WP-CLI dey install go inside /usr/local/bin, wey no dey that list. The job go start, fail sharp-sharp, and the log go show only one line:
/bin/sh: 1: wp: not foundMake you check cron environment by yourself instead of to dey guess. Add dis temporary line:
*/5 * * * * env > /tmp/cron-env.txt 2>&1Read /tmp/cron-env.txt after one interval, then delete the line. The PATH= value wey dey inside that file na exactly wetin your job dey see.
Two ways dey to fix am. Use the absolute path /usr/local/bin/wp, as we talk for step 3. Or set the PATH one time for the top of the crontab, before all your job lines:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binThe same wahala dey wait for you one level down. The wp phar dey start with #!/usr/bin/env php, so the shell must fit find php too. If PHP dey outside /usr/bin—wetin dey happen with custom builds and control panel builds—you go see dis:
/usr/bin/env: 'php': No such file or directoryCall the interpreter directly for that case, for example /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now.
Why one minute interval dey bring back the original wahala
Dis na the third time wey e fail. * * * * * dey feel safer pass five minutes, and for busy site, e go just carry you go back where you start. If one run take time pass the interval, the next run go start while the first one still dey go. Ten minutes later, ten PHP processes go full ground, each one dey hold im own memory and im own database connection.
Check for pileup directly:
ps -eo etimes,user,args | grep '[c]ron event run'etimes na the process age for seconds. One line mean say everything dey okay. Plenty lines wey get age wey pass your interval mean say runs dey stack on top each other, and for small VPS, dis one go end as MySQL Too many connections error, or as the kernel go kill PHP to free memory, wey you fit confirm with sudo dmesg -T | grep -i 'killed process'.
WP-CLI dey run the event callbacks directly instead of requesting wp-cron.php, so the 60 second lock wey WordPress dey use against duplicate spawns no dey work here. flock -n for the step 3 entry na wetin dey stop overlap now. If run skip, e go exit immediately and silently, na so dem design am.
Choose the interval from the shortest schedule wey you truly need, and measure one run first:
time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-nowFive minutes na sensible default: post wey dem schedule for 09:00 go publish by 09:05. Fifteen minutes dey okay for site wey no get anything wey must happen sharp-sharp. One minute na for stores and queue-driven plugins wey truly need am, and only after you don know say one run dey finish for few seconds.
If you no fit install WP-CLI
Some hosting providers dey block shell tools. One simple HTTP request to wp-cron.php fit run the same queue, just say e go pass through the full web stack:
*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/nullThings wey you go lose, clearly:
- The process get limit based on web server and PHP-FPM request timeouts, so long jobs fit cut off for middle.
- The certificate must dey valid or
curlgo stop withSSL certificate problem, so make sure say renewals dey work well with Certbot on nginx. - Page caching no suppose cache
wp-cron.php, if not, cron requests go just get cached response and nothing go run. - You no go see output per event, so the only way to know say job run na to check the effect wey e get.
-sS dey make curl silent if everything go well, but e go still print errors if problem show; this one na wetin you want for cron job.
Wetin else suppose dey for di server schedule
Once system cron don take over di WordPress queue, make you keep di rest of di box routine work for di same place, wey you go fit see am. Operating system security patches suppose dey under unattended upgrades instead of make you dey maintain cron line by hand. WordPress plugin and theme updates na different matter: wp plugin update --all inside crontab fit break your live site for 3am when nobody dey watch, so make you run dat one wit sense, or make you pass am through staging step and backup first.
FAQ
If I disable WP-Cron, e go stop scheduled posts from publish?
No, e no go stop am, as long as something else dey run the queue. DISABLE_WP_CRON only dey stop page loads from trigger the queue. Events still dey schedule exactly as before. If you set post for 09:00, e go publish for the first cron run after 09:00, so if you set five minute interval, e go publish by 09:05. If you set the constant but you no add the cron entry, the post go just stay for the list as Missed schedule until something run the queue.
Which user suppose run the WordPress cron job?
The user wey own the files wey PHP dey write, na im be www-data for default Ubuntu install. Check with stat -c '%U %G' /srv/www/example.com/wp-content/uploads and compare am with the user = line for your PHP-FPM pool config. If you run the job as root, WP-CLI go stop with YIKES error, and if you force am with --allow-root, e go leave files wey root own inside wp-content wey the web server no go fit write later.
How often system cron suppose run WordPress cron?
Every five minutes dey okay for most sites. Make the interval match the shortest schedule wey you truly need, and make sure e pass the time wey one run dey take; you fit measure that one by putting time for front of the WP-CLI command. If you use one minute interval, the runs go stack on top each other for busy site unless flock dey guard dem.
Why wp cron test dey fail after I disable WP-Cron?
Because that command dey test visitor-triggered spawning, and e go report error when DISABLE_WP_CRON dey set to true. That one na the correct result for server wey you configure like that. Check the system cron path instead: read /var/log/wp-cron/example.log, or schedule marker event with wp cron event schedule and confirm say e don comot from wp cron event list after the next run.
I need WP-CLI, or curl to wp-cron.php dey enough?
Curl dey work, and e be the right answer if you no fit install WP-CLI. E dey slow small, because e dey load WordPress through the web server, and request timeout dey limit am. WP-CLI dey run the events for command line PHP process wey no get web timeout, and e dey print one line per event with how long e take, so the log go tell you exactly wetin run and how long e take.