Logo run_as_root - Magento B2B Agency Würzburg

Cron Jobs in Magento 2: Adjusting Schedules and Groups

Technical
Cron Jobs in Magento 2: Adjusting Schedules and Groups

Contents

In today's article, I'll cover a topic that's not too tricky, but can be a bit annoying without some useful tips. Specifically, we'll dive into Magento 2 (Mage-OS / Adobe Commerce) to discuss cron jobs, their configurations, and the process of rescheduling them and altering their execution group.

You might wonder, why bother changing the schedule or group of a cron job? Let's find out.

Why Adjust the Cron Schedule?

Occasionally, a cron job might execute too frequently, leading to server load issues or even deadlocks. On the other hand, if it doesn't run often enough, you could be left waiting for emails that take an hour to send. This scenario becomes particularly tricky when these cron jobs are associated with third-party extensions.

Let's imagine we are using third-party Feed and Mailing extensions, and we are not satisfied with their cron job schedules.

How can you modify the execution patterns of these cron jobs without tampering with the third-party code directly? By implementing changes in your own codebase – your module. Let's explore this next.

Rescheduling Cron Jobs

Let's have a look at the cron job of the Feed extension feed_generation.

Here is its definition:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        ...
        <job name="feed_generation" instance="\Feed\Cron\FeedGenerator" method="execute">
            <schedule>* * * * *</schedule>
        </job>
        ...
    </group>
</config>

Executing every minute, this job can impose a significant server load. We certainly don't need a new feed every 60 seconds!

To modify its frequency, we'll craft a module, Devto_ChangeFeedSchedule, and incorporate an etc/config.xml file within.

So, the structure would look like this:

Devto_ChangeFeedSchedule/
│
├── etc/
│   ├── config.xml      # our config file
│   └── module.xml
│
├── registration.php
│
└── composer.json

The etc/config.xml should contain:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <crontab>
            <default> <!-- cron group -->
                <jobs>
                    <feed_generation> <!-- job name -->
                        <schedule>
                            <cron_expr>0 * * * *</cron_expr> <!-- new cron execution schedule -->
                        </schedule>
                    </feed_generation>
                </jobs>
            </default>
        </crontab>
    </default>
</config>

And last but not least – add a sequence into etc/module.xml to declare dependency over the Feed module and ensure configs are applied in the right order:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Devto_ChangeFeedSchedule">
        <sequence>
            <!-- makes Devto_ChangeFeedSchedule configs be applied after Feed extension configs -->
            <module name="Vendor_FeedExtesnion"/>
        </sequence>
    </module>
</config>

Once the module Devto_ChangeFeedSchedule is activated and the cache cleared, your new job schedule will take effect – running every hour instead of every minute.

But what about cron groups? Let's delve into that next.

The Need to Change Cron Group

Imagine we're still working with the Feed and Mailing third-party extensions. These extensions utilize distinct cron groups, each operating as an isolated process. Thanks to this, cron groups can run concurrently, using individual processes.

Now, suppose these two cron jobs conflict, resulting in data inconsistencies in the Feed and Emails. Worse yet, deadlocks can crop up. You might ask, "Why the deadlocks?"

Well, imagine these extensions read & write the same table, say sales_order_item. When they run simultaneously, the stage is set for potential deadlocks. 🤷‍♂️

If only these jobs were executed sequentially! By relocating them to a shared group, you can ensure they no longer run in parallel.

Altering the Cron Job's Group

A straightforward (though not perfect) solution would be to transfer the Mailing extension's cron job to the default group, where the Feed extension's job resides. Here's the Mailing cron job definition:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="mailing_extension"> <!-- mailing cron group -->
        ...
        <job name="send_mails_from_mailing_extension"
             instance="\Mailing\Cron\SendMailsCron" method="execute">
            <schedule>*/20 * * * *</schedule>
        </job>
        ...
    </group>
</config>

However, a small, non-intuitive trick is essential here: Simply changing the group by redefining crontab.xml won't work. Instead, you need to craft a new cron job while deactivating the old one. This step is necessary since crontab.xml configurations (especially groups) can't be overridden, only augmented.

For instance, merely defining the send_mails_from_mailing_extension cron job in the default group would make it run in both the default and mailing_extension groups.

To disable send_mails_from_mailing_extension, an easy trick is to schedule it for February 30th – a date that doesn't exist!

🤓

Nerd fact: February 30 actually happened in Sweden in 1712.

Following our earlier approach, we'd add the following content to the etc/config.xml in our module, Devto_ChangeMailingJobGroup:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <crontab>
            <default>
                <jobs>
                    <!-- Disable cron execution by scheduling it to Feb 30th. -->
                    <send_mails_from_mailing_extension>
                        <schedule>
                            <cron_expr>0 0 30 2 *</cron_expr>
                        </schedule>
                    </send_mails_from_mailing_extension>
                </jobs>
            </default>
        </crontab>
    </default>
</config>

Next, define a NEW cron job in the default group:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default"> <!-- default cron group -->
        ...
        <!-- added devto prefix to cron name -->
        <job name="devto_send_mails_from_mailing_extension"
             instance="\Mailing\Cron\SendMailsCron" method="execute">
            <schedule>*/20 * * * *</schedule>
        </job>
        ...
    </group>
</config>

And do not forget to add the Mailing module sequence into etc/module.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Devto_ChangeMailingJobGroup">
        <sequence>
            <!-- makes Devto_ChangeMailingJobGroup configs be applied after Mailing extension configs -->
            <module name="Vendor_MailingExtesnion"/>
        </sequence>
    </module>
</config>

After all changes, the module structure should look like this:

Devto_ChangeMailingJobGroup/
│
├── etc/
│   ├── config.xml      # our config file
│   ├── crontab.xml     # our crontab definition file
│   └── module.xml
│
├── registration.php
│
└── composer.json

Once Devto_ChangeMailingJobGroup is active and the cache refreshed, the send_mails_from_mailing_extension job will be disabled. Meanwhile, its counterpart, devto_send_mails_from_mailing_extension, will operate in the default cron group alongside the Feed cron job.

A Word of Caution on the Default Group

It's vital to recognize that Magento's native cron jobs already densely populate the default cron group. Overloading it might cause "traffic jams" if:

  • Jobs have lengthy runtimes: If your tasks take too long to finish, they could end up waiting in line, slowing things down.
  • Overfrequent Scheduling: Setting jobs to run too often can result in backlogs and potential overlaps.
  • Overpopulation: Filling the group with too many tasks, even if they are quick, might overwhelm the system.

So, while the default group is convenient, treat it like a city's main highway during rush hour. It can handle a lot, but there's a limit before things get clogged. If you have too many heavy tasks or they're scheduled too frequently, you'll likely end up with delays.

In short, be mindful of the workload you're placing on the default group and consider creating your own cron groups to avoid conflict of processes.

Conclusion

And that's the lowdown on Magento's cron jobs, how to shuffle their schedules, and adjust their groups without diving into third-party code directly. With the knowledge you've gathered here, tweaking those pesky cron jobs should be much easier 😊

Thank you for reading!


Image from Enrico Da Prato on Unsplash

Optimize Your Magento 2 Cron Jobs

Connect with Vladyslav, our Magento expert, to enhance your store's performance by fine-tuning cron job schedules and groups.

Book your slot now

Related Articles

Technical
images/blog/why_upgrading_your_software_is_more_than_just_a_chore.webp
Vlad Podorozhnyi Avatar

Vlad Podorozhnyi

25.11.25

Why Upgrading Your Software Is More Than Just a Chore
Technical
/images/blog/blog_INP_corewebvitals_thumbnail.webp
Jordy Scholing Avatar

Jordy Scholing

14.11.25

Debugging INP: from DevTools to RUM Data