WordPress has its own scheduler, WP-Cron, for publishing scheduled posts, sending emails, running backups, clearing caches and plugin tasks. It has a design quirk: it only runs when someone visits the site. On every page load, WordPress checks whether tasks are due and, if so, fires a background request to wp-cron.php.

That causes two problems:

  • Low-traffic sites: no visitors at 3 a.m. means the 3 a.m. backup doesn’t run, and scheduled posts publish late (“Missed schedule”).
  • High-traffic sites: the check runs on every request and can spawn overlapping cron runs, adding load.

On a VPS the fix is simple: disable the page-load trigger and run cron from the system scheduler.

1. Disable the page-load trigger

In wp-config.php, above “That’s all, stop editing!”:

define( 'DISABLE_WP_CRON', true );

Or with WP-CLI:

sudo -u www-data wp config set DISABLE_WP_CRON true --raw

This doesn’t disable scheduled tasks — it only stops visitors from triggering them. You must now run them yourself (step 2), or nothing scheduled will happen.

Edit the web user’s crontab:

sudo crontab -u www-data -e
*/5 * * * * cd /var/www/example.com && /usr/local/bin/wp cron event run --due-now --quiet

Every five minutes, WP-CLI runs all due events directly in PHP-CLI — no HTTP request, no web server timeouts, no PHP-FPM worker tied up. Use command -v wp to confirm the path.

Running as www-data ensures files created by tasks (backups, cache files) have the right owner.

2b. Or request wp-cron.php over HTTP

If WP-CLI isn’t available:

*/5 * * * * curl -s -o /dev/null https://example.com/wp-cron.php?doing_wp_cron

This goes through the web server like a visitor would, so PHP-FPM time limits apply. Long tasks may be cut off — WP-CLI is preferable.

2c. Or a systemd timer

For logging in the journal and no overlapping runs:

/etc/systemd/system/wp-cron-example.service:

[Unit]
Description=WP-Cron for example.com

[Service]
Type=oneshot
User=www-data
WorkingDirectory=/var/www/example.com
ExecStart=/usr/local/bin/wp cron event run --due-now --quiet

/etc/systemd/system/wp-cron-example.timer:

[Unit]
Description=Run WP-Cron every 5 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers | grep wp-cron

See systemd timers.

Multisite

Each site in a network has its own cron queue. Run them all:

*/5 * * * * cd /var/www/network && for url in $(/usr/local/bin/wp site list --field=url --archived=0 --deleted=0); do /usr/local/bin/wp cron event run --due-now --url="$url" --quiet; done

Inspect and debug

sudo -u www-data wp cron event list                  # all scheduled events and next run
sudo -u www-data wp cron event list --fields=hook,next_run_relative,recurrence
sudo -u www-data wp cron event run woocommerce_cleanup_sessions   # run one now
sudo -u www-data wp cron test                        # checks that WP-Cron can spawn (when not disabled)
sudo -u www-data wp cron schedule list               # available intervals

Signs of trouble:

  • Events with next_run far in the past → cron isn’t running; check the crontab and grep CRON /var/log/syslog.
  • Thousands of events for the same hook → a misbehaving plugin keeps scheduling duplicates; clean up and report it.
  • “Missed schedule” on posts → cron didn’t run at publish time.

The WP Crontrol plugin offers the same overview in wp-admin.

How often?

Every 5 minutes suits most sites. Every minute is fine for stores or sites with time-sensitive tasks (scheduled sales, membership expirations). Running more often than tasks are due costs almost nothing with --due-now.

Heavy tasks

Backups, imports and newsletter sends can run for minutes. With WP-CLI there’s no web timeout, but watch memory and make sure runs don’t overlap. Wrapping the command in flock prevents that:

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

Real cron is one of the small changes that make WordPress more reliable on a WordPress VPS — something shared hosting often doesn’t allow.

  • WordPress
  • Cron
  • Performance