Magento 2 Cron Jobs: Setup, Troubleshooting, and External Monitoring with Cloud Cron

Magento 2 runs 53+ cron jobs for indexing, emails, and catalog updates. Learn how to set up cron correctly, fix common failures, and monitor with Cloud Cron.

Start Free 20 checks free forever

Magento 2 ships with over 50 cron jobs out of the box. Not optional background tasks — required ones. Indexing, email queues, currency rate updates, abandoned cart cleanup, sitemap generation, catalog price rules, customer segment matching. Stop cron for 24 hours on a busy store and the admin panel starts showing "One or more indexers are invalid." Stop it for a week and product prices, stock levels, and search results go stale. Customers see outdated data. Orders break.

WordPress treats cron as a convenience. Magento treats it as infrastructure.

What Magento 2 schedules and how

Magento organizes its cron jobs into three groups, each with its own configuration in crontab.xml:

default — the workhorse group. Runs catalog indexing, email sending, currency rate imports, sitemap generation, product alert notifications, and cache cleanup. Most of the 53+ jobs live here.

index — dedicated to Magento's indexer system. Catalog product, catalog category, catalog search, price, and stock indexers all run on their own schedule. When these stop, the admin shows "Reindex required" warnings and the storefront serves stale data.

consumers — message queue processing. Bulk API operations, inventory updates, order exports. Added in Magento 2.3.

Each group runs through bin/magento cron:run --group=<name>. The standard crontab setup calls cron:run without a group flag, which processes all three sequentially.

All scheduled work goes into the cron_schedule database table. Magento writes future jobs there based on each job's cron expression, then marks them as pending, running, success, missed, or error. You can query it directly:

SELECT job_code, status, COUNT(*) as cnt
FROM cron_schedule
GROUP BY job_code, status
ORDER BY cnt DESC;

If you see hundreds of missed entries, cron isn't firing reliably.

The correct crontab setup (and what changed in 2.4)

Magento 2 provides a command to generate its own crontab entries:

bin/magento cron:install

This adds three lines to the system crontab:

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

The first line is the one that matters. It runs every minute and processes all scheduled jobs. The second handles Magento's component manager updates. The third manages setup-related cron tasks.

Important change in 2.4: Magento used to ship pub/cron.php as an HTTP-triggered alternative for hosts without shell access. As of Magento 2.4.0, that file is deprecated and removed. The only supported method is bin/magento cron:run via CLI. This means shared hosting without SSH access and without crontab access is effectively incompatible with Magento 2.4+.

If you're on managed or shared hosting where you can't edit the system crontab, you need an external cron service like Cloud Cron that hits an HTTP endpoint on a schedule. More on that below.

Common failures and how to diagnose them

Three patterns account for most Magento cron problems.

Cron never starts. The system crontab entry is missing or the PHP path is wrong. Check with crontab -l and verify the PHP binary path matches your environment. On systems with multiple PHP versions, /usr/bin/php might point to PHP 7.4 while Magento 2.4.6+ requires 8.1+. The cron runs, hits a version error, and dies silently.

Cron starts but jobs stay "missed." Magento pre-generates the schedule 20 minutes ahead. If cron:run doesn't fire within 15 minutes of the scheduled time, jobs flip to missed. The usual cause: the previous run is still going when the next one starts, and the lock file blocks it. On underpowered servers with heavy indexing, one cron:run can take 5+ minutes, and the missed jobs pile up fast.

Check for stuck processes:

ps aux | grep 'cron:run'

If you see multiple zombie processes, you've found the bottleneck.

Indexer errors after cron stops. The "One or more indexers are invalid" message in admin is the loudest symptom. Magento's indexers run on schedule — when cron stops, indexes go stale and the storefront shows outdated prices and stock. Running bin/magento indexer:reindex fixes it once, but the problem comes back if cron isn't stable. This is exactly the kind of silent failure that cron monitoring catches before customers notice.

For deeper debugging of silent cron failures, check var/log/magento.cron.log and the cron_schedule table together. The log shows PHP-level errors; the table shows scheduling-level problems.

Shared hosting without SSH: the pub/cron.php problem

Before 2.4, store owners on shared hosting could point an external cron service at https://store.com/pub/cron.php and it worked. That file loaded Magento and ran the cron queue over HTTP.

Magento 2.4 killed that approach. Official position: use CLI cron only. But plenty of stores run on hosting without SSH access or crontab control.

Some managed Magento hosts (Nexcess, Cloudways, Magento Commerce Cloud) set up cron for you automatically. But if yours doesn't, and you can't SSH in, you need a workaround.

The workaround: create a minimal PHP script that calls cron:run internally. Drop it in pub/ or a protected directory:

<?php
// pub/run-cron.php — external cron trigger
// Protect with a secret token
if ($_GET['token'] !== 'your-secret-token-here') {
    http_response_code(403);
    exit;
}

$php = PHP_BINARY;
$magento = dirname(__DIR__) . '/bin/magento';
exec("$php $magento cron:run 2>&1", $output, $code);
http_response_code($code === 0 ? 200 : 500);
echo implode("\n", $output);

Then point Cloud Cron at https://store.com/pub/run-cron.php?token=your-secret-token-here with a one-minute schedule. Not elegant, but it works. Cloud Cron will both trigger the script and alert you if it starts returning errors. Make sure to exclude this file from your CDN cache.

For stores that can use CLI but want to run cron without managing a server, an external scheduler like WatchCron handles the timing while you focus on the store.

The cron_schedule table: cleanup matters

Magento writes every scheduled, executed, and failed job to cron_schedule. On active stores, this table grows fast — 10,000+ rows per day isn't unusual with all groups running every minute.

Magento cleans this table automatically: successful jobs are removed after 60 minutes, failed ones after 600 minutes. But I've seen stores where the cleanup job itself was broken, so cron_schedule grew to 2 million rows. Queries slowed down, cron scheduling slowed down, more jobs got missed. A feedback loop that's hard to spot without monitoring.

Quick health check:

SELECT COUNT(*) FROM cron_schedule;
SELECT status, COUNT(*) FROM cron_schedule GROUP BY status;

If total rows exceed 100,000, something's wrong with cleanup. Truncate the table and investigate why the cleanup job isn't running.

Monitor Magento cron with WatchCron Cloud Cron

Setting up cron is half the problem. Knowing when it breaks is the other half.

Cloud Cron works two ways with Magento:

If you have a cron URL (the pub/run-cron.php script above, or a similar endpoint): Cloud Cron hits it on schedule and monitors the HTTP response. A 500 means cron failed. A timeout means the server is overloaded. You get an alert through Slack, Telegram, email, or any other channel before customers notice stale prices or missing order confirmation emails.

If you use server crontab: pair your existing cron:run entry with heartbeat monitoring. Add a curl ping at the end of your crontab line:

* * * * * /usr/bin/php /var/www/magento/bin/magento cron:run && curl -fsS https://ping.watchcron.com/your-monitor-id > /dev/null

If cron:run fails (non-zero exit code), the && prevents the ping, and WatchCron alerts you after the grace period. If the server itself goes down, the ping stops entirely.

Either approach catches the same failure modes: crashed PHP processes, full disks blocking log writes, hosting provider silently disabling cron after a resource spike. The difference between finding out in 5 minutes versus finding out when a customer emails about a stuck order.

For Magento stores running alongside WordPress (common in content-commerce setups), the WooCommerce cron guide covers the WordPress side. Use separate monitors for each — they fail independently.

Frequently asked

Every minute. Magento's default cron:install sets * * * * * for a reason — indexers, email queues, and message consumers all expect minute-level scheduling. Running less frequently causes jobs to pile up and increases the chance of missed executions.

Yes, but you need an HTTP endpoint since Magento 2.4 removed pub/cron.php. Create a token-protected PHP script that calls bin/magento cron:run internally, then point WatchCron's Cloud Cron at that URL. It will both trigger your cron and alert you if it starts failing.

Magento's indexers run as cron jobs. When cron stops, indexes go stale and Magento flags them as invalid. Run bin/magento indexer:reindex to fix it immediately, then investigate why cron stopped. The fix is temporary if cron remains broken.

Query the cron_schedule table: SELECT * FROM cron_schedule ORDER BY scheduled_at DESC LIMIT 20. Look for recent entries with success status. If the newest successful entry is older than a few minutes, cron isn't running. Also check var/log/magento.cron.log for PHP errors.

For most stores, a single bin/magento cron:run handles all groups fine. High-traffic stores with heavy indexing might benefit from splitting groups: default and consumers every minute, index every 5 minutes. Use separate crontab entries with the --group flag, and set up separate WatchCron monitors for each.