If you've ever kicked off a large product import in Magento 2 and watched the progress bar crawl, you know the pain. A 50,000-product CSV that should take minutes ends up running for hours. Exports grind the database to a halt. Scheduled imports time out. It doesn't have to be this way.
In this guide we'll cover every meaningful lever you can pull to make Magento 2 imports and exports dramatically faster — from server-side tuning to code-level tricks.
Why Magento 2 Imports Are Slow
Before optimizing, it's worth understanding why imports are slow by default:
-
EAV overhead: Every product attribute write touches
catalog_product_entity_*tables multiple times. - Reindex on import: By default, Magento triggers reindexing after each batch, not after the full import.
- URL rewrite generation: Each imported product generates URL rewrites — one of the heaviest operations.
- Image processing: Magento copies and resizes images synchronously during import.
-
Event observers: Third-party modules often hook into
catalog_product_save_afterand fire on every imported row. - Database locking: Large inserts cause table-level locks that block other queries.
Now let's fix all of that.
1. Use the Built-in Import UI Correctly
The Admin import UI (System > Data Transfer > Import) is fine for occasional imports but has limitations:
-
Upload limit: PHP's
upload_max_filesizeandpost_max_sizemust be increased for large files. -
Timeout: Apache/Nginx timeouts kill long-running imports. Set
max_execution_time = 0inphp.inifor CLI, and configure Nginx timeouts for web uploads:
fastcgi_read_timeout 600;
proxy_read_timeout 600;
-
Behavior setting: Always use
Add/Updateunless you intend to delete.Replaceis much slower because it deletes and re-inserts.
For anything above 10,000 products, use the CLI.
2. Run Imports via CLI
The CLI import avoids PHP-FPM timeouts entirely:
bin/magento import:run \
--entity=catalog_product \
--behavior=add_update \
--validation-strategy=validation-stop-on-errors \
--allowed-error-count=10 \
--separator=',' \
--multiple-value-separator=',' \
--fields-enclosure='"' \
/path/to/products.csv
This runs in the CLI PHP context where you can set unlimited execution time.
3. Disable Reindexing During Import
This is the single biggest optimization. Switch all indexes to manual update mode before importing:
bin/magento indexer:set-mode schedule catalog_product_flat
bin/magento indexer:set-mode schedule catalog_product_price
bin/magento indexer:set-mode schedule catalog_search
bin/magento indexer:set-mode schedule catalogrule_product
After the import completes, reindex everything at once and switch back:
bin/magento indexer:reindex
bin/magento indexer:set-mode realtime catalog_product_flat
# ... etc
Reindexing once after a 50,000-product import is orders of magnitude faster than reindexing after every batch of 100.
4. Optimize MySQL for Bulk Imports
By default, MySQL is tuned for transactional workloads, not bulk inserts. Temporarily adjust these settings before a large import:
SET GLOBAL innodb_flush_log_at_trx_commit = 2;
SET GLOBAL sync_binlog = 0;
SET GLOBAL innodb_autoinc_lock_mode = 2;
Add to my.cnf for a more permanent performance gain:
innodb_buffer_pool_size = 4G # 70-80% of available RAM
innodb_log_file_size = 512M
innodb_flush_method = O_DIRECT
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
bulk_insert_buffer_size = 64M
Important: Reset
innodb_flush_log_at_trx_commit = 1in production for durability. Only relax it during controlled import windows.
5. Skip URL Rewrites When Possible
URL rewrite generation is one of the most expensive parts of import. If you're doing a backend-only import (products not yet live, or you're updating prices/stock only), disable it:
In app/etc/env.php, temporarily set:
'catalog' => [
'frontend' => [
'flat_catalog_product' => 0,
],
],
Or better — import only the columns you need. A CSV with just sku, price, and qty skips URL rewrite generation entirely because Magento only regenerates rewrites when URL-key fields change.
Pro tip: Split your import file into two CSVs:
-
products_core.csv— sku, name, description, attributes (triggers URL rewrites) -
products_stock_price.csv— sku, price, qty only (no URL rewrites)
Import stock/price updates separately and save significant time.
6. Batch Size Tuning
Magento imports in batches. The default batch size is typically 100–200 rows. Increasing this reduces the number of database round-trips:
In vendor/magento/module-import-export/Model/Import.php, the constant DEFAULT_SIZE controls this — but modifying core is bad practice. Instead, create a plugin:
// app/code/Vendor/ImportOptimizer/Plugin/ImportPlugin.php
public function afterGetBunchSize(\Magento\ImportExport\Model\Import $subject, int $result): int
{
return 500; // Increase from default ~100
}
Test with 500–1000 depending on your row width and available memory. Larger batches reduce overhead but increase memory per batch.
7. Disable Non-Essential Observers During Import
Third-party modules frequently attach observers to product save events. During a CLI import, you almost certainly don't need newsletter triggers, ERP sync hooks, or marketing tag updates firing 50,000 times.
Create a custom CLI command that wraps the import and temporarily disables specific observers, or use Magento's areas configuration to exclude them from crontab area context.
At minimum, identify your heaviest observers:
bin/magento dev:events:list | grep catalog_product_save
Then check the module list and disable non-critical ones during import windows.
8. Image Import Optimization
If your CSV includes images, Magento processes them synchronously — download (if URL), copy to pub/media/catalog/product, and add to the queue for resizing.
Best practice: Import product data first (without images), then import images separately in a second pass. This allows you to:
- Use a dedicated import window for image processing
- Run image resize asynchronously via queue consumers
- Retry failed image downloads without re-importing all product data
Enable async image resizing:
bin/magento config:set catalog/product/flat_catalog_product 0
bin/magento config:set system/upload_configuration/enable_resize_file 0
Then process the image resize queue separately:
bin/magento queue:consumers:start media.storage.catalog.image.resize
9. Export Optimization
Exports in Magento 2 are often even slower than imports because they join across many EAV tables.
Enable flat catalog before large exports:
bin/magento config:set catalog/frontend/flat_catalog_product 1
bin/magento indexer:reindex catalog_product_flat
With flat catalog enabled, exports read from a single denormalized table instead of joining dozens of EAV tables.
Use direct database queries for custom exports: If you need a non-standard export (e.g., just SKU + price + stock for an ERP), writing a custom script that queries catalog_product_entity + cataloginventory_stock_item directly will always outperform the Magento export framework.
10. Monitor and Benchmark
Before and after your optimizations, measure:
# Time a full import
time bin/magento import:run --entity=catalog_product ...
# Watch MySQL in real-time during import
mysqladmin -u root -p extended-status -r -i 1 | grep -E 'Queries|Threads'
# Check slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
The combination of schedule-mode indexing, MySQL tuning, and disabled URL rewrites on price-only imports typically yields 5–10x speedups on large catalogs.
Putting It All Together: The Import Checklist
Before running a large import:
- [ ] Switch relevant indexers to
schedulemode - [ ] Disable non-essential observers
- [ ] Tune MySQL buffer pool and log settings
- [ ] Split CSV: content vs. price/stock
- [ ] Set
max_execution_time = 0in CLI php.ini - [ ] Use CLI import, not Admin UI
- [ ] Disable image processing if possible
After import:
- [ ] Run
bin/magento indexer:reindex - [ ] Run
bin/magento cache:flush - [ ] Switch indexers back to
realtimeif desired - [ ] Process image resize queue
- [ ] Verify product visibility in frontend
Conclusion
Magento 2 import performance is a layered problem — no single fix solves everything. The biggest wins come from schedule-mode indexing (eliminates mid-import reindex), MySQL buffer tuning (faster bulk inserts), and splitting CSV files by operation type (avoids unnecessary URL rewrite generation).
Once you've implemented these optimizations, a 50,000-product import that took 3 hours should complete in 15–30 minutes. That's the kind of improvement that makes a real difference in release windows, data migrations, and daily sync pipelines.
Have a specific import bottleneck you're hitting? The techniques above cover the vast majority of cases — but profiling with EXPLAIN on the slow queries during import is always a good next step.
Top comments (0)