Magento 2 Reindex & Index Management: The Complete Guide
Everything that actually matters about Magento 2 indexing: the two modes, how Update by Schedule works under the hood, the CLI you need, and how to clear a stuck "index locked" reindex.
Reindexing in Magento 2 is how the store turns slow, normalized EAV data into fast, flat lookup tables that the storefront reads on every request. Prices, category contents, search results, and stock all come from indexes — not live joins.
Get index management wrong and the symptoms are maddening: a product you can see in admin is missing on the frontend, a price change never appears, or a full reindex jams with “index locked.” This guide covers the whole model on Magento 2.4.4 — 2.4.9, and the fixes for the problems everyone hits.
What the indexers do
Magento stores catalog data in the EAV model — flexible, but slow to read because a single product spans many tables. Indexers run those joins once and write the result into flat tables the storefront queries directly. Magento Open Source ships nine:
design_config_grid— design config admin gridcustomer_grid— customer admin gridcatalog_category_product/catalog_product_category— which products are in which categorycatalogrule_rule/catalogrule_product— catalog price rulescatalog_product_price— final prices (the heavy one)cataloginventory_stock— stock statuscatalogsearch_fulltext— the OpenSearch/Elasticsearch search index
When the underlying data changes, the matching index goes stale and must be rebuilt. How it rebuilds is the index mode.
Update on Save vs Update by Schedule
This is the single most important setting, and 2.4.8 changed its default.
| Mode | How it reindexes | Trade-off |
|---|---|---|
| Update on Save | Reindexes the affected rows immediately when you save in admin or via API. | Changes appear instantly, but every save is slower and admin saves can stall during bulk imports. |
| Update by Schedule | Records the change in a *_cl changelog table; cron reindexes only the changed rows in batches. | Saves are fast and server load is smooth, but changes appear only after cron runs (a 1–2 minute lag). Requires working cron. |
Use Update by Schedule on production — it is why a busy store stays responsive during imports and price changes. As of Magento 2.4.8 it is the default for every indexer, including customer_grid, which previously only supported Update on Save.
How Update by Schedule works under the hood
This is the MView (materialized view) mechanism, and understanding it makes every “why didn't my change show up” mystery obvious.
- You save a product. A database trigger writes the product id into a changelog table named after the index, suffixed
_cl— for examplecatalog_product_price_cl. - The
indexer_update_all_viewscron job compares the latestversion_idin the_cltable against the value stored inmview_state. - If they differ, it reindexes only the changed ids and advances
mview_state. Theindexer_reindex_all_invalidcron handles indexers explicitly marked invalid.
Two consequences fall out of this: if cron is not running, scheduled indexes never update (see why Magento cron stops firing), and if a changelog table grows huge, reindexing slows down — usually a sign cron has been down for a while.
The CLI you actually need
Check what mode each indexer is in and whether anything is stale:
bin/magento indexer:status
bin/magento indexer:show-modeSwitch modes (do this with the site in maintenance mode and cron paused to avoid deadlocks):
# everything to scheduled (recommended for production)
bin/magento indexer:set-mode schedule
# a single indexer back to realtime
bin/magento indexer:set-mode realtime catalog_product_priceReindex on demand — all indexers, or just one:
bin/magento indexer:reindex
bin/magento indexer:reindex catalog_product_price cataloginventory_stockBefore indexer:set-mode schedule on a live store, enable maintenance mode and stop cron. Flipping modes while cron and traffic are writing to the same tables is a classic cause of database deadlocks.
Fixing “Index locked by another reindex process”
This is the most-searched Magento indexing error. It means a previous reindex started, grabbed a lock, and died before releasing it — killed by a timeout, an out-of-memory error, or a deploy. The lock and the indexer's working status never got cleared, so every new reindex refuses to start.
The reliable fix
# 1) reset the stuck indexer state
bin/magento indexer:reset
# 2) clear stale lock files
rm -rf var/locks/*
# 3) reindex, with extra memory if it died on memory before
php -d memory_limit=4G bin/magento indexer:reindexIf a single indexer is stuck rather than all of them, reset just that one: bin/magento indexer:reset catalog_product_price. After the reset its status flips to invalid, which is expected — the next reindex rebuilds it cleanly. I hit exactly this recently on a category indexer after a killed process; reset plus a clean reindex cleared it in seconds.
“Index locked” is a stuck-state problem, not a data problem. indexer:reset + clear var/locks/ + reindex fixes the overwhelming majority of cases. If it recurs, the underlying reindex is dying — check memory limits and the exception log.
Writing a custom indexer
When a module needs its own flat data, you register an indexer in indexer.xml and its MView changelog in mview.xml. The action class implements IndexerActionInterface and MviewActionInterface:
<?php
declare(strict_types=1);
namespace Panth\Catalog\Model\Indexer;
use Magento\Framework\Indexer\ActionInterface;
use Magento\Framework\Mview\ActionInterface as MviewActionInterface;
class StockScore implements ActionInterface, MviewActionInterface
{
public function executeFull(): void
{
// rebuild the entire panth_stock_score index table
}
public function executeList(array $ids): void
{
// reindex only these entity ids (called by Update on Save)
}
public function executeRow($id): void
{
$this->executeList([(int) $id]);
}
public function execute($ids): void
{
// called by the MView cron with the changed ids from the _cl table
$this->executeList(array_map('intval', (array) $ids));
}
}The mview.xml subscription tells Magento which table to watch (it creates the _cl changelog and triggers automatically), so your custom index participates in Update by Schedule exactly like the core ones.
Frequently asked questions
How do I reindex everything in Magento 2?
Run bin/magento indexer:reindex from the Magento root. To rebuild a specific indexer, append its name, e.g. bin/magento indexer:reindex catalog_product_price. Check bin/magento indexer:status first to see which indexers are actually stale before rebuilding all of them.
What is the difference between Update on Save and Update by Schedule?
Update on Save reindexes affected rows immediately when you save, so changes appear instantly but each save is heavier. Update by Schedule records the change in a *_cl changelog table and lets cron reindex it in batches — lighter and smoother for production, with a 1–2 minute lag. Magento 2.4.8 made Update by Schedule the default for all indexers.
Why is my price or product change not showing on the frontend?
If the indexer is in Update by Schedule mode, the change only appears after cron runs the indexer_update_all_views job — so the usual cause is cron not running. Confirm cron is firing, check indexer:status for stale/invalid indexers, and as a one-off run bin/magento indexer:reindex to force it.
How do I fix "Index locked by another reindex process"?
A prior reindex died holding the lock. Run bin/magento indexer:reset (or reset the single stuck indexer), delete stale locks with rm -rf var/locks/*, then reindex — bump memory with php -d memory_limit=4G bin/magento indexer:reindex if it died on memory before.
Should I run reindex from cron or manually?
On production, set all indexers to Update by Schedule and let cron handle reindexing automatically — never schedule a blanket indexer:reindex in crontab, which rebuilds everything and fights the MView cron. Manual reindex is for one-off fixes, deployments, or recovering a stuck indexer.
Indexers stuck, slow, or silently stale? I tune Magento indexing, MView, and cron so your catalog stays correct and fast — fixed-fee from $499 audit · $2,499 sprint · ~Nh @ $25/hr. See performance optimization.
Get your indexing sorted