How to Install, Run, and Configure Magento 2 Cron Jobs

How to Install, Run, and Configure Magento 2 Cron Jobs

[Updated: March 18, 2026]

A single misconfigured cron job can stall your entire Magento store. Indexes stop updating, emails never send, and customers see stale prices.

This guide covers everything from initial cron setup to custom job creation, real troubleshooting commands, and performance tuning for production servers.

Key Takeaways

  • Magento 2 uses three cron groups (index, default, consumers) to organize scheduled tasks across your store.
  • Version 2.4.8 introduced automatic cron_schedule table cleanup and changed all new indexers to "Update by Schedule" mode.
  • Custom cron jobs require a crontab.xml file in your module and an executable PHP class with an execute() method.
  • The bin/magento cron:run command must run twice: once to discover tasks and again to execute them.
  • Cron frequency, schedule lifetime, and history cleanup settings control both task timing and database table size on your server.

What Are Magento 2 Cron Jobs?

Magento 2 cron jobs = scheduled tasks that run at set intervals to keep your store operational. They handle indexing, email delivery, currency updates, and dozens of background processes.

Perfect for: store owners running production Magento instances, developers building custom modules, DevOps teams managing server infrastructure

Not ideal for: static sites without dynamic pricing, stores on fully managed PaaS that abstracts cron away

Magento 2 relies on the Unix cron daemon to trigger bin/magento cron:run at regular intervals. Each execution checks the cron_schedule database table for pending jobs and processes them in order.

Without cron running, your store breaks in predictable ways. Product prices stop updating. Transactional emails queue up but never send. Search indexes fall behind, and customers see outdated results.

Cron Groups in Magento 2

Magento organizes cron jobs into three groups. Each group runs on its own schedule and can be configured with separate settings.

Group Purpose Examples
index Reindexing operations Catalog, price, search index updates
default General maintenance tasks Email sending, currency rates, sitemap generation, log cleaning
consumers Message queue processing Async operations, bulk API, inventory updates

You can run a specific group in isolation:

bin/magento cron:run --group=index

This separation matters for performance. Index rebuilds are resource heavy. Running them in a dedicated group prevents slow indexing from blocking email delivery or other lightweight tasks.

How to Install Magento 2 Cron Jobs

Step 1: Connect to Your Server

Log into your Magento server via SSH as the filesystem owner. All Magento CLI commands must run under this user account.

ssh magento-user@your-server.com

Step 2: Navigate to the Magento Root Directory

cd /var/www/html/magento2

Replace the path with your actual Magento installation directory.

Step 3: Install the Cron Tab Entry

bin/magento cron:install

This adds a crontab entry for the current user. Use --force to overwrite an existing entry:

bin/magento cron:install --force

Step 4: Verify the Installation

crontab -l

You should see an entry like:

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

This runs every minute by default. Magento handles internal scheduling through the cron_schedule table, so the one minute interval is intentional.

Step 5: Remove the Cron Tab (When Needed)

bin/magento cron:remove

This removes the Magento crontab entry without affecting other cron jobs on the server.

How to Run Cron Jobs Manually

Run the cron command twice. The first execution discovers pending tasks. The second execution processes them.

bin/magento cron:run
bin/magento cron:run

Check execution results in the log file:

tail -50 var/log/cron.log

Manual execution is useful for testing custom cron jobs or debugging scheduling issues. In production, always rely on the automated crontab entry.

How to Configure Cron Settings

Admin Panel Configuration

Navigate to Stores > Configuration > Advanced > System > Cron (Scheduled Tasks).

Each cron group exposes these settings:

Setting Default Purpose
Generate Schedules Every 1 minute How often cron creates new schedule entries
Schedule Ahead for 4 minutes How far in advance to schedule jobs
Missed if Not Run Within 2 minutes When a job gets "missed" status
History Cleanup Every 10 minutes How often old schedule entries are purged
Success History Lifetime 60 minutes How long successful job records persist
Failure History Lifetime 600 minutes How long failed job records persist

Command Line Configuration

For more control, edit the crontab entry to adjust execution frequency:

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

This example runs every 5 minutes instead of every minute. The five fields in cron syntax represent:

┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-7, 0 and 7 = Sunday)
* * * * *

Creating Custom Cron Jobs

Custom cron jobs require two components: a PHP class with the business logic and a crontab.xml file that registers the schedule.

Step 1: Create the Executable Class

Place your class in your module's Cron directory:

<?php
namespace Vendor\Module\Cron;

use Psr\Log\LoggerInterface;

class CustomTask
{
    private LoggerInterface $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function execute(): void
    {
        $this->logger->info('Custom cron job executed');
        // Your business logic here
    }
}

Step 2: Register the Job in crontab.xml

Create etc/crontab.xml in your module:

<?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">
        <job name="vendor_module_custom_task"
             instance="Vendor\Module\Cron\CustomTask"
             method="execute">
            <schedule>0 */6 * * *</schedule>
        </job>
    </group>
</config>

This example runs every 6 hours. Change the group id to index or consumers based on the task type.

Step 3: Compile and Clear Cache

bin/magento setup:di:compile
bin/magento cache:clean

Step 4: Verify the Job

Run cron twice, then check the cron_schedule table:

SELECT * FROM cron_schedule
WHERE job_code = 'vendor_module_custom_task'
ORDER BY created_at DESC LIMIT 5;

Custom Cron Groups

For jobs that need isolated scheduling, create etc/cron_groups.xml:

<?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="custom_group">
        <schedule_generate_every>1</schedule_generate_every>
        <schedule_ahead_for>4</schedule_ahead_for>
        <schedule_lifetime>2</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>

Setting use_separate_process to 1 runs this group in its own PHP process, preventing resource heavy custom jobs from blocking core Magento tasks.

Cron Job Monitoring and Logging

Magento writes cron activity across four log files:

Log File Content
var/log/cron.log Primary cron execution log. Check here first.
var/log/exception.log Jobs that threw exceptions (error status)
var/log/support_report.log Detailed failure reports for crashed jobs
var/log/debug.log Missed job notifications (developer mode only)

The cron_schedule database table stores the complete execution history:

SELECT job_code, status, created_at, scheduled_at, executed_at, finished_at
FROM cron_schedule
ORDER BY scheduled_at DESC LIMIT 20;

Status values in the table: pending, running, success, missed, error.

For production stores, consider adding server monitoring tools that alert on cron failures before they cascade into visible store issues.

Troubleshooting Magento 2 Cron Jobs

Cron Jobs Not Running

Check if the crontab entry exists:

crontab -l | grep magento

If empty, reinstall with bin/magento cron:install --force.

Verify PHP path:

which php

The path in your crontab must match your server's PHP binary. Mismatched paths (for example, /usr/bin/php vs /usr/local/bin/php) are a common cause of silent failures.

Jobs Stuck in "running" Status

Jobs can get stuck when PHP processes crash mid execution. Find and clear them:

SELECT * FROM cron_schedule WHERE status = 'running';

Reset stuck jobs:

UPDATE cron_schedule SET status = 'error'
WHERE status = 'running'
AND executed_at < DATE_SUB(NOW(), INTERVAL 1 HOUR);

cron_schedule Table Bloat

On busy stores, the cron_schedule table can grow to millions of rows and slow down cron execution itself.

Check table size:

SELECT COUNT(*) FROM cron_schedule;

Manual cleanup:

DELETE FROM cron_schedule
WHERE scheduled_at < DATE_SUB(NOW(), INTERVAL 7 DAY);

Magento 2.4.8 addresses this with automatic cleanup of entries for jobs that no longer exist in the system. For older versions, schedule a manual cleanup or reduce history_success_lifetime in the admin configuration.

Jobs in "missed" Status

A "missed" status means the job was scheduled but not executed within the Missed if Not Run Within window. Common causes:

  1. Cron daemon not running or stopped
  2. Previous cron execution took too long, delaying the schedule
  3. Server resource exhaustion (CPU or memory)

Check your Magento CLI output for memory or timeout errors when running cron.

Performance Optimization for Cron Jobs

Cron job configuration has a direct impact on server resource usage. Every minute, cron:run loads the Magento framework, checks the schedule table, and executes pending jobs.

Frequency Tuning

The default one minute interval suits most stores. For smaller stores with fewer scheduled tasks, running every 5 minutes reduces server load without noticeable impact.

For high traffic stores, keep the one minute interval but separate resource heavy jobs into custom cron groups with use_separate_process enabled.

Multi Node Considerations

On clustered Magento hosting setups, run the crontab on ONE node only. Running cron across multiple nodes causes duplicate job execution and database lock contention.

Index Mode Impact

Magento 2.4.8 defaults all new indexers to "Update by Schedule" mode instead of "Update on Save." This includes the Customer Grid indexer, which before 2.4.8 only supported "Update on Save." This shift moves reindexing work from real time (during admin saves) to cron execution. Monitor your index cron group to make sure reindex jobs complete within their scheduled window.

Database Optimization

The cron_schedule table benefits from periodic optimization. Run OPTIMIZE TABLE cron_schedule during low traffic periods to reclaim space after large deletes.

Keep history_success_lifetime and history_failure_lifetime as low as practical. For most stores, 60 minutes for successes and 600 minutes for failures provides enough diagnostic data without table bloat.

Securing Magento 2 Cron Jobs

File System Permissions

Restrict cron file execution to the Magento filesystem owner. No other user should be able to modify or execute cron related files.

chmod 750 bin/magento
chown magento-user:magento-group bin/magento

HTTP Access Protection

The legacy cron.php file was removed in Magento 2.4.0. All cron execution now happens through the CLI. If you upgraded from an older version, verify that cron.php is not accessible via HTTP.

Block access in your Nginx configuration:

location ~ ^/(cron|setup|dev) {
    deny all;
    return 403;
}

Environment Variables

Store sensitive configuration (database credentials, API keys) in env.php or server environment variables. Never hardcode credentials in cron job classes.

Audit Trail

Review cron_schedule entries for unexpected job codes. Third party extensions can register cron jobs that run with full Magento framework access. Audit installed extensions and remove unused ones to minimize the cron attack surface.

What Changed in Magento 2.4.8

Magento 2.4.8 (released April 8, 2025, supported until April 2028) introduced four cron related improvements (full release notes):

  1. Automatic cron_schedule cleanup. The system now removes schedule entries for jobs that no longer exist in the codebase. This prevents orphaned entries from accumulating after extension removal.

  2. Indexers default to "Update by Schedule." All new indexers use schedule mode instead of "Update on Save." This reduces real time processing during admin operations but increases cron workload.

  3. Customer Grid indexer supports both modes. Before 2.4.8, the Customer Grid indexer only supported "Update on Save." It now supports both modes and defaults to "Update by Schedule." After upgrading, verify your indexer mode: bin/magento indexer:show-mode customer_grid.

  4. Changelog table management. The system removes unused changelog tables when switching indexer modes, keeping the database clean.

These changes require PHP 8.3 or PHP 8.4. PHP 8.1 support was removed in 2.4.8, and PHP 8.2 is supported for upgrades only.

Next Steps

Proper cron configuration keeps your Magento store running without manual intervention. Start with the default settings, monitor your cron.log for errors, and adjust scheduling as your store grows.

For stores that need zero cron management overhead, managed Magento hosting handles cron setup, monitoring, and optimization as part of the infrastructure.

FAQ

What happens if Magento cron stops running?

Indexes fall behind, transactional emails stop sending, currency rates freeze, and the cron_schedule table fills with pending jobs. Product prices and catalog search results become stale.

How often should Magento cron run?

Every minute is the recommended default. Magento manages internal job scheduling through the cron_schedule table. The one minute crontab interval ensures jobs execute close to their scheduled time.

Can I run cron on multiple servers in a cluster?

Run the crontab on one node only. Multiple nodes executing cron causes duplicate job processing and database lock conflicts. Use a dedicated cron node or a process manager to enforce single execution.

How do I check if a specific cron job ran?

Query the cron_schedule table: SELECT * FROM cron_schedule WHERE job_code = 'your_job_code' ORDER BY scheduled_at DESC LIMIT 10;. Check the status column for success, error, or missed.

What is the difference between cron groups?

Groups isolate jobs with different scheduling needs. The index group handles reindexing, default covers general tasks like emails and cleanup, and consumers process message queue operations. Each group can have independent timing and lifetime settings.

How do I create a custom cron job in Magento 2?

Create a PHP class with an execute() method, then register it in your module's etc/crontab.xml file with a schedule expression. Run bin/magento setup:di:compile and clear cache to activate.

Why are my cron jobs showing "missed" status?

The job was scheduled but not executed within the configured window. Check that the cron daemon is running, previous jobs are not blocking execution, and the server has sufficient CPU and memory resources.

How do I clean up the cron_schedule table?

Reduce history_success_lifetime and history_failure_lifetime in admin settings. For immediate cleanup, run DELETE FROM cron_schedule WHERE scheduled_at < DATE_SUB(NOW(), INTERVAL 7 DAY);. Magento 2.4.8 auto cleans orphaned entries.

Does cron configuration affect store performance?

Yes. Each cron execution loads the full Magento framework. Resource heavy jobs (reindexing, bulk operations) running during peak traffic compete with web requests for CPU and memory. Schedule heavy jobs during low traffic windows.

What PHP version does cron require in Magento 2.4.8?

Magento 2.4.8 requires PHP 8.3 or PHP 8.4. The CLI PHP binary used in crontab must match the web server PHP version. Mismatched versions cause unexpected behavior.

CEO & Co-Founder

Raphael Thiel co-founded MGT-Commerce in 2011 together with Stefan Wieczorek and has built it into a leading Magento hosting provider serving 5,000+ customers on AWS. With 25+ years in e-commerce and cloud infrastructure, he oversees hosting architecture for enterprise clients. He also co-founded CloudPanel, an open-source server management platform.


Get the fastest Magento Hosting! Get Started