WordPress has a built-in task scheduling system called WP-Cron. It handles things like publishing scheduled posts, checking for updates and running background tasks created by plugins.

The strange thing about WP-Cron is that, by default, it doesn’t run on a proper schedule.

Instead, WordPress checks whether any tasks are due when somebody visits the website.

This works reasonably well on busy websites, but it can be unreliable on sites with very little traffic.

If nobody visits the website for several hours, scheduled tasks can be delayed until somebody eventually turns up.

I wanted something more predictable, so I replaced the default behaviour with a scheduler that runs independently of website traffic.

The performance problem

There’s another issue with relying on visitors to trigger scheduled tasks: performance.

When WordPress notices that a cron event is due, it makes a separate HTTP request to wp-cron.php to execute the scheduled tasks.

That request is supposed to be non-blocking, so the visitor shouldn’t have to wait for all the cron jobs to finish before their page loads.

But the work still needs to happen somewhere.

If a plugin starts a particularly expensive task, such as generating a backup, it can consume CPU, database resources and PHP workers that would otherwise be available for normal website requests.

On a busy website, that can mean visitors experiencing slower responses or requests timing out because the server is busy processing background jobs.

There’s also some overhead in WordPress initiating the cron request itself, which isn’t always as non-blocking as intended.

I prefer to separate scheduled tasks from visitor requests entirely. Running them through a proper scheduler makes their timing predictable and avoids relying on somebody visiting the website to kick things off.

It doesn’t eliminate the resource usage, but it gives me more control over when that work happens.

Disabling the default WP-Cron

The first step is to stop WordPress from triggering cron checks on page requests.

Add this to wp-config.php:

1define('DISABLE_WP_CRON', true);
Copied!

This doesn’t remove WordPress’s scheduled events. It just prevents WordPress from automatically trying to run them when somebody visits the website.

You now need something else to execute those events.

Using WP-CLI

WordPress provides a WP-CLI command which runs scheduled events that are due.

1wp cron event run --due-now
Copied!

This checks the registered cron events and runs those which are ready to execute.

The advantage is that it doesn’t require an HTTP request to the website, and can be triggered directly by a scheduler.

You can see what’s scheduled using:

1wp cron event list
Copied!

This is also useful when debugging a plugin that isn’t completing its background tasks.

Scheduling the command with EasyEngine

I’m using EasyEngine to manage WordPress installations, so rather than creating a traditional Linux crontab entry, I can use EasyEngine’s own scheduler.

1ee cron create example.com \
2 --command='wp cron event run --due-now' \
3 --schedule='@every 10m'
Copied!

This runs the WordPress cron command every ten minutes, regardless of whether anyone has visited the website.

EasyEngine uses Ofelia to manage scheduled jobs inside its Docker environment, which saves having to manually execute commands inside the correct container.

It also prevents overlapping executions of the same scheduled job, something that can be useful when a task takes longer than expected.

Ten minutes is fine for some websites, but not everything. If a website relies on tasks running close to their scheduled time, I’d use a shorter interval.

Checking the schedule

You can list the scheduled jobs using:

1ee cron list
Copied!

I also check the WordPress cron event list to make sure scheduled events are being processed.

It’s worth checking after a migration, particularly if WordPress has been moved from a conventional hosting environment into Docker.

The site can appear to be working normally while scheduled tasks quietly stop running.

Why bother?

For a small blog, a delayed scheduled post might not seem like a particularly big problem.

But WP-Cron is also used by plugins for background processing, and those tasks can be more important than publishing an article at exactly the right time.

Moving the scheduler outside WordPress means its execution no longer depends on incoming traffic.

It’s a small configuration change that makes the behaviour much more predictable. There’s no reason for the number of people visiting a website to determine whether its background jobs run.