PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Cron in Magento 2 – groups, custom jobs, admin schedule, debugging

by Henryk Tews / Tuesday, 13 April 2021 / Published in Magento 2

Magento 2’s cron system is responsible for dozens of background tasks – reindexing, sending emails, cleaning cache, synchronising with external systems. Writing your own cron job is straightforward, but understanding groups, scheduling, and how to debug when a job is not running saves hours. I show the complete picture.

How Magento cron works

Magento cron requires a single system cron entry that fires every minute:

* * * * * www-data /usr/bin/php /var/www/html/bin/magento cron:run 2>&1 | grep -v "Ran jobs by schedule"

This command reads the cron schedule table in the database, finds jobs that are due, and executes them. In DDEV it is set up automatically.

Defining your own cron job

<!-- etc/crontab.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">

    <group id="default">
        <!-- Run every hour -->
        <job name="vendor_module_sync_products"
             instance="Vendor\Module\Cron\SyncProducts"
             method="execute">
            <schedule>0 * * * *</schedule>
        </job>

        <!-- Schedule configurable from admin panel -->
        <job name="vendor_module_export_orders"
             instance="Vendor\Module\Cron\ExportOrders"
             method="execute">
            <config_path>vendor_module/cron/export_orders_schedule</config_path>
        </job>
    </group>

    <!-- Custom group runs separately from the default group -->
    <group id="vendor_module">
        <job name="vendor_module_heavy_import"
             instance="Vendor\Module\Cron\HeavyImport"
             method="execute">
            <schedule>0 2 * * *</schedule>
        </job>
    </group>

</config>

Cron class implementation

<?php

declare(strict_types=1);

namespace Vendor\Module\Cron;

use Psr\Log\LoggerInterface;

class SyncProducts
{
    public function __construct(
        private \Vendor\Module\Model\ProductSyncService $syncService,
        private LoggerInterface $logger
    ) {}

    public function execute(): void
    {
        $this->logger->info('Starting product sync');

        try {
            $result = $this->syncService->sync();
            $this->logger->info('Product sync complete', [
                'synced'  => $result->getSyncedCount(),
                'errors'  => $result->getErrorCount(),
                'elapsed' => $result->getElapsedSeconds() . 's',
            ]);
        } catch (\Exception $e) {
            // Log and swallow - a cron exception should not kill the entire cron run
            $this->logger->error('Product sync failed: ' . $e->getMessage(), [
                'exception' => $e,
            ]);
        }
    }
}

Cron groups – isolation and configuration

<!-- etc/cron_groups.xml - configure your custom group -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/cron_groups.xsd">
    <group id="vendor_module">
        <schedule_generate_every>15</schedule_generate_every>
        <schedule_ahead_for>20</schedule_ahead_for>
        <schedule_lifetime>15</schedule_lifetime>
        <history_cleanup_every>10</history_cleanup_every>
        <history_success_lifetime>60</history_success_lifetime>
        <history_failure_lifetime>600</history_failure_lifetime>
        <use_separate_process>1</use_separate_process>
    </group>
</config>

Debugging cron – when a job is not running

# Check the cron schedule table in the database
bin/magento cron:run --group default
bin/magento cron:run --group vendor_module

# Run a specific job directly (bypasses scheduling)
bin/magento cron:run --job-code vendor_module_sync_products

# Check job history in the database
SELECT job_code, status, scheduled_at, executed_at, finished_at, messages
FROM cron_schedule
WHERE job_code = 'vendor_module_sync_products'
ORDER BY scheduled_at DESC
LIMIT 20;

# Statuses: pending, running, success, missed, error

# Check if cron is generating schedules
SELECT COUNT(*) FROM cron_schedule WHERE status = 'pending';
# Should be > 0; if 0 - cron process is not running

# Clean stale cron entries
bin/magento cron:install   # reinstall system crontab

Configurable schedule from admin panel

<!-- etc/adminhtml/system.xml -->
<section id="vendor_module">
    <group id="cron">
        <label>Cron Settings</label>
        <field id="export_orders_schedule" type="text">
            <label>Order Export Schedule (cron expression)</label>
            <comment>e.g. */30 * * * * (every 30 minutes)</comment>
        </field>
    </group>
</section>

Summary

Magento 2 cron is reliable when set up correctly. The most common issues are: missing system cron entry, jobs in “missed” state because the previous run took too long, and missing logging that makes debugging hard. Custom groups provide isolation – a failing job in your group does not affect the default group. Always log start, end and key metrics from your cron jobs.

About Henryk Tews

What you can read next

Xdebug – configuration, PHPStorm, debugging Magento plugins
Strategy pattern in PHP – and how Magento 2 uses it in pricing

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}