Independent Editorial DeskWordPress Releases, Builds, and Operations
Back to Archive
Hardening NotesImplementation Notes

WordPress Cron Event Example: Schedule Plugin Tasks Safely

Schedule recurring WordPress plugin tasks safely with custom cron intervals, duplicate prevention, runtime locks, cleanup, and WP-CLI testing.

Published

April 21, 2026

Reading Time

3 min read

Updated

April 21, 2026

Abstract clock and scheduled task panel representing WordPress cron events.
Control LedgerHardening Notes

Hardening Notes

Baselines, access reduction, and default settings that stand up in production.

Best For

Teams preparing, launching, or maintaining WordPress as a backend service in a production stack.

Primary Topics

Hardening NotesImplementation Notes

Editorial Focus

Control Ledger: Baselines, access reduction, and default settings that stand up in production. Updated on April 21, 2026.

Full Report

Last reviewed: April 21, 2026

WP-Cron is often blamed for unreliable background work, but many failures come from plugin code that schedules events incorrectly. The most common bug is scheduling the same event repeatedly on page load until thousands of duplicate jobs exist.

This article shows how to schedule a recurring plugin task safely, add a custom interval, prevent duplicate events, and clean up on deactivation.

Best fit

Use WP-Cron for lightweight maintenance tasks that can run shortly after their scheduled time: refreshing cached data, cleaning old logs, syncing a small feed, or sending a low-volume report. Do not use it as a precise job scheduler for high-volume queues or time-critical billing workflows.

Register a custom interval

<?php
/**
 * Plugin Name: VulnWP Scheduled Sync
 * Description: Example recurring plugin task with WP-Cron.
 * Version: 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_filter( 'cron_schedules', 'vulnwp_add_sync_interval' );

function vulnwp_add_sync_interval( $schedules ) {
	$schedules['vulnwp_every_fifteen_minutes'] = array(
		'interval' => 15 * MINUTE_IN_SECONDS,
		'display'  => __( 'Every 15 minutes', 'vulnwp-cron' ),
	);

	return $schedules;
}

Schedule the event once

const VULNWP_SYNC_HOOK = 'vulnwp_run_scheduled_sync';

register_activation_hook( __FILE__, 'vulnwp_schedule_sync_event' );
register_deactivation_hook( __FILE__, 'vulnwp_unschedule_sync_event' );

add_action( VULNWP_SYNC_HOOK, 'vulnwp_run_scheduled_sync' );

function vulnwp_schedule_sync_event() {
	if ( ! wp_next_scheduled( VULNWP_SYNC_HOOK ) ) {
		wp_schedule_event(
			time() + MINUTE_IN_SECONDS,
			'vulnwp_every_fifteen_minutes',
			VULNWP_SYNC_HOOK
		);
	}
}

function vulnwp_run_scheduled_sync() {
	// Fetch, clean, cache, or sync a small unit of work.
	update_option( 'vulnwp_last_sync_at', current_time( 'mysql', true ) );
}

The wp_next_scheduled() check prevents duplicate scheduling. Do not schedule events unconditionally on every request.

Unschedule on deactivation

function vulnwp_unschedule_sync_event() {
	$timestamp = wp_next_scheduled( VULNWP_SYNC_HOOK );

	if ( $timestamp ) {
		wp_unschedule_event( $timestamp, VULNWP_SYNC_HOOK );
	}
}

Cleanup matters because WordPress can continue trying to execute a hook after the plugin code is no longer active. That creates noise, errors, and support confusion.

Test with WP-CLI

wp cron event list --fields=hook,next_run,recurrence
wp cron event run vulnwp_run_scheduled_sync
wp option get vulnwp_last_sync_at

Manual execution is useful before deployment. It proves that the callback runs independently from normal page traffic.

Add a simple runtime lock

WP-Cron can overlap when a task takes longer than expected or when traffic triggers several requests around the same time. Use a short lock for jobs that should not run concurrently.

function vulnwp_run_scheduled_sync() {
	if ( get_transient( 'vulnwp_sync_lock' ) ) {
		return;
	}

	set_transient( 'vulnwp_sync_lock', 1, 10 * MINUTE_IN_SECONDS );

	try {
		update_option( 'vulnwp_last_sync_at', current_time( 'mysql', true ) );
	} finally {
		delete_transient( 'vulnwp_sync_lock' );
	}
}

The lock should expire automatically in case PHP dies before cleanup. Keep the expiration longer than the expected job duration but short enough that a failed run does not block the feature all day.

Failure handling

Cron callbacks should fail quietly for visitors but visibly for operators. Store the last error message or timestamp in an option, send a safe admin notice, or expose the problem through a custom Site Health check. Do not echo output from cron callbacks.

Production checklist

  • Register custom schedules before scheduling events that use them.
  • Use wp_next_scheduled() to prevent duplicates.
  • Keep cron callbacks small and idempotent.
  • Log failures without exposing secrets.
  • Unschedule plugin-owned events on deactivation.
  • Use system cron to trigger WP-Cron on serious production sites.

Common mistakes

  • Scheduling on every page load. This creates duplicate jobs.
  • Using custom intervals before registering them. WordPress will reject unknown schedules.
  • Doing too much work in one event. Split heavy work into smaller units.
  • No deactivation cleanup. Stale hooks keep failing after plugin removal.
  • Expecting exact timing. WP-Cron depends on visits unless system cron triggers it.

Related reading

This implementation guide pairs with our WP-Cron reliability checklist and the plugin uninstall cleanup example.

References and further reading

Popular Guides

Popular WordPress guides to read next.

These articles connect recurring production concerns: implementation details, updates, troubleshooting, recovery paths, and operational cleanup.

Continue Reading

More from the archive.

Diagnostic dashboard scene representing a WordPress Site Health review before major updates.
01Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Site Health Check Before Major Updates: What to Review First

A pre-update WordPress Site Health checklist covering loopbacks, connectivity, debug settings, and environment readiness.

Structured data and route review scene representing permalink validation after a WordPress migration.
02Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Permalink Checklist After Migration: Catch URL Problems Early

A post-migration WordPress permalink checklist for checking rewrite rules, post URLs, archives, and redirect noise.

Technical media workspace representing image preparation and optimization before upload to WordPress.
03Build Pattern
Implementation Notes

Build Pattern

Extension points, code paths, and implementation choices that should survive contact with production.

May 21, 2026 · 3 min read

WordPress Image Optimization Checklist: What to Fix Before Upload

A practical WordPress image optimization checklist covering dimensions, compression, formats, and Media settings before upload.