SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-29

How to disable wp-cron and use system cron for WordPress

WP-Cron dey slow down site because e only run when person visit. Learn how to use WP-CLI and system crontab to run tasks properly so your site no go dey lag or miss jobs.

Wetin be wp-cron, and why system cron dey replace am

WP-Cron na di 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 itssef for /wp-cron.php to do di work. If you move dat job go system cron, you go get one predictable run for fixed schedule, weda di site get one thousand visitors for dat minute or zero.

Two lines dey do di main work: one constant for wp-config.php, and one crontab entry. Every oda thing for dis guide na di part wey dem two lines no tell you. Which user di job must run as, how to prove say di scheduled events truly fire, and di three ways wey di setup fit fail without printing anything for di site.

Di examples dey use /srv/www/example.com as di WordPress directory and www-data as di web server user. Change di paths and user to your own for every place.

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 go wait for the result. But one PHP worker go 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 fit happen 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 visitors wey come at the same time no go start run. The lock dey cap duplication. E no dey move the work comot from the request path.

Count how many times e dey fire for your own server before you decide whether e matter. Every loopback dey show for the web server access log:

sudo grep -c 'wp-cron.php' /var/log/nginx/access.log

Apache dey write go /var/log/apache2/access.log instead. If the count reach thousands per day, e be real cost, and na that 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 go start behave like the quiet site wey dey below.

Wetin dey cause make cron break for site wey no get plenty visitor

If visitor no show, cron no go run. Site wey only get small-small visit per day go run im scheduled jobs only when person visit, and e go happen for any random time wey dat visit occur.

All the wahala dey look the same. Post wey you schedule for 09:00 go just dey the post list dey show Missed schedule until person load page. Backup plugins go skip the night. Update checks go slow, so the dashboard no go show say update dey even if security release don drop. Order emails, renewal notices, and expiry warnings go delay.

None of this one dey log error. For WordPress eye, the job no delay at all, because the job no even start.

Step 1: off the visitor trigger for wp-config.php

Open /srv/www/example.com/wp-config.php come add dis constant:

define( 'DISABLE_WP_CRON', true );

Put am top of the line wey read /* That's all, stop editing! Happy publishing. */, sake of say the line wey dey just under dat comment need wp-settings.php, and wp-settings.php na where WordPress dey hook the cron check onto init. If you define constant after dat require, e go too late to change anything, and the file go look correct but the trigger go still dey run.

Confirm say the line dey where you think e dey:

grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.php

Dis constant no dey stop events from to dey scheduled. Plugins go still dey add jobs to the queue, exactly as before. E only dey stop page loads from to run dat queue, wey mean say the 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 no dey usually cause wahala sake of say the file only dey run wetin be due. To block am for your web server config na optional. If you block am, the curl fallback wey dey near the end of dis guide go stop to 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-cli

Install 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 --info

php 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 limit. Update am later with 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 version

If 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 inside di first failure mode wey we list down.

Also note say sudo -u www-data -i no go 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 right 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.conf

For default Ubuntu install, both answer na www-data. If you give the site im own PHP-FPM pool with im own user, wey be wetin per-site setup for a LAMP stack on Ubuntu 24.04 usually end up, use that user for everything wey follow.

Make log directory wey that user fit write:

sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cron

Edit that user im crontab:

sudo crontab -u www-data -e

Add 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>&1

Make we break am down. */5 dey run am every five minutes. flock -n dey take lock file and e dey stop immediately if previous run still hold am. /usr/local/bin/wp na the absolute path, wey cron need. --path dey 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 practice. Cron dey mail job output go the user, most VPS images no get mail transfer agent installed, and cron go log (CRON) info (No MTA installed, discarding output) then throw the output away. File dey keep the evidence.

Check the file wey you save:

sudo crontab -u www-data -l

For plenty sites, use one line each with staggered minutes so dem no go start all at once:

*/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>&1

The 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: confirm say the scheduled events actually run

If you save one crontab line, e no mean say e don work. Start from the one wey easy to check reach the one wey go show you say everything dey okay.

First, cron start the command? Cron dey log go the journal under im own unit:

journalctl -u cron.service --since "15 min ago" | grep wp

One correct 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.log

WP-CLI dey print one line for every 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. Most times, nothing go dey to run, so the file go empty. Make sure you read the file after one run wey you know say get work to do.

Third, confirm 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_check

Wait 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 if you run am, e no go do anything else for the site. If the hook still dey the list after two intervals, e mean say the queue no dey run. The first two checks go tell you whether the wahala 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 if DISABLE_WP_CRON be true. For server wey you configure well, that error na wetin you suppose see, e no mean say something spoil.

Di systemd timer alternative

If di oda scheduled work for di box dey run as systemd services and timers, put WordPress for inside too. Every run go show for systemctl list-timers, and di output go go inside journal instead of 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-now

Den /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.target
sudo 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 20

Systemd no go run two copies of di same service at di same time, so dis version no need flock. Persistent=true make am catch up on any run wey miss sake of say di machine off, someting wey crontab entry no fit do.

Choose either crontab or di timer. If you run both for di same site, e mean say dem dey drain di queue two times, and your customers go see duplicate runs of email or order jobs.

Why the cron job must not run as root

Dis 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 dis:

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-content

Root 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 of dem:

sudo crontab -u root -l
sudo crontab -u www-data -l

Why cron dey report wp: not found

Dis one na the second time e fail. Cron dey give user jobs very short PATH, /usr/bin:/bin. WP-CLI dey install 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 found

Make you check cron environment by yourself instead of dey guess. Add dis temporary line:

*/5 * * * * env > /tmp/cron-env.txt 2>&1

Read /tmp/cron-env.txt after one interval, then delete the line. The PATH= value wey dey 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 show for step 3. Or set the PATH one time for the top of your crontab, before all the job lines:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

The same wahala dey wait you for 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, wey dey happen with custom builds and control panel builds, you go see dis:

/usr/bin/env: 'php': No such file or directory

Call 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, but for site wey busy, 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, you go get ten PHP processes, each one dey hold im own memory and im own database connection.

Check for pileup like dis:

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 up, and for small VPS, dat one go end as MySQL Too many connections error, or as the kernel go kill PHP to free memory, wey you fit confirm wit 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 apply here. flock -n for the step 3 entry na wetin dey prevent overlap now. If run skip, e go exit immediately and silently, na so dem design am. Overlap na problem wey you must solve for crontab, no be something wey host kernel go solve for you: even the cache aware task placement wey dem add for Linux kernel 7.2 na only which core process go land e dey decide, e no dey decide how many of dem you start.

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-now

Five minutes na sensible default: post wey you 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 hosts dey block shell tools. One simple HTTP request to wp-cron.php go 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/null

Things wey you go lose, plainly:

  • The run get limit based on web server and PHP-FPM request timeouts, so long job fit cut off for middle.
  • The certificate must dey valid or curl go stop with SSL certificate problem, so make sure renewals dey work with Certbot on nginx.
  • Page caching no must cache wp-cron.php, or cron requests go get cached response and nothing go run.
  • You no go get output for every event, so the only evidence say job run na the effect wey e produce.

-sS dey keep curl quiet when e succeed while e still dey print errors, na this one you want for cron job.

Wetin else suppose dey for server schedule

Once system cron don take over WordPress queue, make you keep the rest of the box routine work for the 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 for crontab fit break your live site for 3am when nobody dey watch, so make you run that one with sense, or make you pass am through staging step and backup first.

FAQ

If I disable WP-Cron, scheduled posts go stop to publish?

No, as long as something else dey run the queue. DISABLE_WP_CRON only stop page loads from triggering the queue. Events still dey scheduled 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, wey 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 take, wey you fit measure if you put time for front of the WP-CLI command. One minute intervals fit make runs pile up 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 this. 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 correct answer if you no fit install WP-CLI. E dey slow, 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.