WP-Cron vs Spawned Background Jobs: How WordPress Really Runs “Background” Work

Robot pointing on a wall

If background work on your WordPress site appears to take longer than you’d expect, the issue may not necessarily be the task, but how WordPress runs it. WP-Cron and spawned background jobs solve similar problems, but they operate very differently, and those differences show up most clearly in tail latency and reliability. Tail latency refers to the slowest few percent of executions that users feel even when the average looks fine.

Understanding where your work runs—and what it’s queued behind—makes it easier to choose the right model.

How WP-Cron Works

Despite the name, WP-Cron is not a real system cron, which would schedule commands to run automatically at specified intervals. Instead, it’s a request-driven scheduler.

  • Cron events are stored in the database (in the wp_options table).
  • They only run when a page is loaded (for example a visitor viewing a page of your site).
  • At this point, WP-Cron checks whether any scheduled events are due.
  • If they are, they execute inside that same PHP request, usually late in the lifecycle (often at shutdown).

In practice, this means that WP-Cron jobs:

  • Share memory, CPU, and execution time with the parent request (of which there are two types – admin or front-end requests).
  • Run after whatever else that request is doing.
  • Can be delayed if the triggering request is slow, blocked, or infrequent.

If the request that fires cron is an admin update, a maintenance-mode flow, a loopback check (when WordPress makes an HTTP request to itself), or something with sleeps, retries, or heavy I/O (Input / Output, for example database queries), then your “background” job politely waits its turn. That waiting time is invisible unless you measure it—but it shows up as high tail latency.

WP-Cron Pros

WP-Cron Cons

  • Not truly asynchronous (it is deferred execution, not asynchronous execution – it still runs in the same PHP process, and still blocks that request until it finishes).
  • Execution timing depends on traffic.
  • Competes with unrelated work in the same request.
  • Can lead to performance issues (increased server load and erratic execution) under heavy traffic because it relies on page loads.

WP-Cron works best when tasks are tiny, idempotent (have an equal result), and non-urgent.

How Spawned Background Jobs Work

Spawned background jobs take a more explicit approach. Instead of running work inline, the parent request:

  • Fires a new HTTP request to a dedicated worker endpoint.
  • Returns immediately (or nearly so).
  • Lets the worker run in isolation.

This can be done via:

  • wp_remote_post() with non-blocking args
  • Loopback requests to a custom REST endpoint (for example using register_rest_route to define an endpoint, then create a callback function that processes the request. Then, trigger the loopback request using wp_remote_post.
  • External job runners or queues (plugins like Action Scheduler can be helpful with this)

The key difference here is isolation. Spawned jobs don’t inherit the parent request’s baggage. They also start fresh, with their own execution window, and aren’t queued behind admin screens, updates, or maintenance logic. Even if the code path is identical, removing that waiting time dramatically improves tail latency.

Spawned job pros

  • More predictable execution timing.
  • Better tail latency for heavier tasks.
  • Easier to reason about performance.
  • Safer for expensive transforms or external API calls.

Spawned job cons

Spawned workers shine when tasks are heavier, bursty, or triggered by long-running parent requests.

What to Consider

If you want to understand where your time is really going, measure:

  • Request kind: inline (WP-Cron) vs spawned worker.
  • Payload size: how much data you serialize, copy, or transform.
  • Prep time vs execution time: how much time is being spent building payloads, batching, or on network or external API time.

Often the surprise isn’t the “send” or “process” phase—it’s the work done before the job even starts running.

Practical Ways to Reduce Tail Latency

These apply to both models, but matter more for inline cron:

  • Trim payloads (drop anything non essential)
  • Coalesce work per request (for example if a higher level action already represents the change, skip redundant per-field updates that are also triggered in the same request).
  • Split create/modify from delete when testing: Deletes often dominate runtime (often triggering more work than updating or creating) and hide the real cost of other operations.
  • Choose the right execution model: WP-Cron for tiny, frequent, low-risk tasks, or a spawned worker for heavy transforms, sync jobs, or anything triggered by slow admin flows.

Final Thoughts

WP-Cron runs inside a request. Spawned background jobs run beside it. If your jobs feel slow, consider not only what work they do, but also where they run. Move heavier tasks to isolated spawned requests, reduce what you process, and coalesce where you can. You should immediately notice the performance improvement.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from Karen Attfield

Subscribe now to keep reading and get access to the full archive.

Continue reading