Tag: Web Server

  • WordPress and the “A scheduled event has failed” error

    WordPress and the “A scheduled event has failed” error

    Every WordPress site administrator knows that feeling. You log into the dashboard, and there’s a message waiting for you: “A scheduled event has failed”. Your heart stops for a moment. Is the site down? Is it a serious crash?

    Calm down! Before you start to panic, take a deep breath. This error, although it sounds serious, rarely means disaster. Most often, it’s simply a signal that the internal task scheduling mechanism in WordPress isn’t working optimally.

    In this article, we’ll explain what this error is, why it appears, and how to fix it professionally in various server configurations.

    What is WP-Cron?

    WordPress needs to perform cyclical background tasks: publishing scheduled posts, creating backups, or scanning the site for viruses (as in the case of the wf_scan_monitor error from the Wordfence plugin). To handle these operations, it uses a built-in mechanism called WP-Cron.

    The problem is that WP-Cron is not a real cron daemon known from Unix systems. It’s a “pseudo-cron” that has a fundamental flaw: it only runs when someone visits your website.

    • On sites with low traffic: If no one visits the site, tasks aren’t performed on time, which leads to errors.
    • On sites with high traffic: WP-Cron is called on every page load, which generates unnecessary server load.

    In both cases, the solution is the same: disable the built-in WP-Cron and replace it with a stable, system-level cron job.

    Scenario 1: A Single WordPress Site

    This is the most basic and common configuration. The solution is simple and comes down to two steps.

    Step 1: Disable the built-in WP-Cron mechanism

    Edit the wp-config.php file in your site’s main directory and add the following line:

    define(‘DISABLE_WP_CRON’, true);

    Step 2: Configure a system cron

    Log into your server via SSH and type crontab -e to edit the list of system tasks. Then, add one of the following lines, which will properly call the WordPress cron mechanism every 5 minutes.

    • wget method: */5 * * * * wget -q -O – https://yourdomain.co.uk/wp-cron.php?doing_wp_cron >/dev/null 2>&1
    • curl method: */5 * * * * curl https://yourdomain.co.uk/wp-cron.php?doing_wp_cron >/dev/null 2>&1

    Remember to replace yourdomain.co.uk with your actual address. From now on, tasks will be executed regularly, regardless of site traffic.

    Scenario 2: Multiple Sites on a Standard Server

    If you manage multiple sites, adding a separate line in crontab for each one is impractical and difficult to maintain. A better solution is to create a single script that will automatically find all WordPress installations and run their tasks.

    Step 1: Create the script file

    Create a file, e.g., /usr/local/bin/run_all_wp_crons.sh, and paste the following content into it. The script searches the /var/www/ directory for wp-config.php files.

    #!/bin/bash
    #
    # Script to run cron jobs for all WordPress sites
    # Optimised for ISPConfig directory structure
    # The main directory where the ACTUAL site files are located in ISPConfig
    SITES_ROOT=”/var/www/clients/”

    # Path to the PHP interpreter (may need to be adjusted)
    PHP_EXECUTABLE=”/usr/bin/php”

    # Logging (optional, but useful for debugging)
    LOG_FILE=”/var/log/wp_cron_runner.log”

    echo “Starting cron jobs (ISPConfig): $(date)” >> $LOG_FILE

    # Find all wp-config.php files and run wp-cron.php for them
    find “$SITES_ROOT” -name “wp-config.php” -print | while IFS= read -r -d ” config_file; do

        # Extract the directory where WordPress is located
        WP_DIR=$(dirname “$config_file”)

        # Extract the user name (e.g., web4) from the path
        # It’s the sixth element in the path /var/www/clients/client4/web4/web/
        WP_USER=$(echo “$WP_DIR” | awk -F’/’ ‘{print $6}’)

        if [ -z “$WP_USER” ]; then
            echo “-> WARNING: Failed to determine user for: $WP_DIR” >> $LOG_FILE
            continue
        fi

        # Check if the wp-cron.php file exists in this directory
        if [ -f “$WP_DIR/wp-cron.php” ]; then
            echo “-> Running cron for: $WP_DIR as user: $WP_USER” >> $LOG_FILE
            # Run wp-cron.php using PHP CLI, switching to the correct user
            su -s /bin/sh “$WP_USER” -c “(cd ‘$WP_DIR’ && ‘$PHP_EXECUTABLE’ wp-cron.php)”
        else
            echo “-> WARNING: Found wp-config, but no wp-cron.php in: $WP_DIR” >> $LOG_FILE
        fi
    done

    echo “Finished: $(date)” >> $LOG_FILE
    echo “—” >> $LOG_FILE

    Step 2: Grant the script execution permissions

    chmod +x /usr/local/bin/run_all_wp_crons.sh

    Step 3: Create a single cron job to manage everything

    Now your crontab can contain just one line:

    */5 * * * * /usr/local/bin/run_all_wp_crons.sh >/dev/null 2>&1

    Scenario 3: Multiple Sites with ISPConfig Panel

    The ISPConfig panel uses a specific directory structure with symlinks, e.g., /var/www/yourdomain.co.uk points to /var/www/clients/client1/web1/. Using the script above could cause tasks to be executed twice.

    To avoid this, you need to modify the script to search only the target clients directory.

    Step 1: Create a script optimised for ISPConfig

    Create the file /usr/local/bin/run_ispconfig_crons.sh. Note the change in the SITES_ROOT variable.

    #!/bin/bash
    # Script to run cron jobs for all WordPress sites
    # Optimised for ISPConfig directory structure
    # We only search the directory with the actual site files
    SITES_ROOT=”/var/www/clients/”

    # Path to the PHP interpreter
    PHP_EXECUTABLE=”/usr/bin/php”

    # Optional log file to track progress
    LOG_FILE=”/var/log/wp_cron_runner.log”

    echo “Starting cron jobs (ISPConfig): $(date)” >> $LOG_FILE

    find “$SITES_ROOT” -name “wp-config.php” -print0 | while IFS= read -r -d $’\0′ config_file; do
        WP_DIR=$(dirname “$config_file”)
        if [ -f “$WP_DIR/wp-cron.php” ]; then
            echo “-> Running cron for: $WP_DIR” >> $LOG_FILE
            (cd “$WP_DIR” && “$PHP_EXECUTABLE” wp-cron.php)
        fi
    done

    echo “Finished: $(date)” >> $LOG_FILE
    echo “—” >> $LOG_FILE

    Steps 2 and 3 are analogous to Scenario 2: give the script execution permissions (chmod +x) and add a single line to crontab -e, pointing to the new script file.

    Summary

    The “A scheduled event has failed” error is not a reason to panic, but rather an invitation to improve your infrastructure. It’s a chance to move from the unreliable, built-in WordPress mechanism to a solid, professional system solution that guarantees stability and performance.

    Regardless of your server configuration, you now have the tools to sleep soundly, knowing that your scheduled tasks are running like clockwork.