Magento 2 Performance Optimization
Magento 2 is heavy: default install does 200–400 SQL queries and 50–100 file operations per page. Without special setup, this gives TTFB 3–8 seconds on average server. Goal—reduce to 200–500ms via proper caching stack, PHP config, and eliminating query bottlenecks.
Production Stack
Target architecture:
CDN (Cloudflare/Fastly) → Varnish → Nginx → PHP-FPM 8.2 → MySQL 8.0
↕ ↕
Redis (cache) Redis (session)
Elasticsearch (catalog search)
Each layer critical. Varnish gives 85–95% hit rate for anonymous users. Redis removes disk operations on cache. Elasticsearch replaces slow MySQL catalog search.
PHP 8.2 + OPcache + JIT
Magento 2.4.6+ officially supports PHP 8.2. Move from 7.4 gives 15–25% gain on CPU-bound operations.
; /etc/php/8.2/fpm/conf.d/opcache.ini
opcache.enable=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=60000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.enable_cli=1
; JIT — gives 10-20% on Magento computational tasks
opcache.jit=tracing
opcache.jit_buffer_size=256M
PHP-FPM pool:
[magento]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm-magento.sock
listen.backlog = 65535
pm = dynamic
pm.max_children = 40
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 2000
php_admin_value[memory_limit] = 768M
php_admin_value[max_execution_time] = 600
php_admin_value[opcache.file_cache] = /tmp/opcache
Production Mode and Deploy
# Switch to production mode
bin/magento deploy:mode:set production
# DI compilation—generates interceptors, removes reflection overhead
bin/magento setup:di:compile
# Deploy static files with minification
bin/magento setup:static-content:deploy ru_RU en_US -j 4
# Reset and warm cache
bin/magento cache:clean && bin/magento cache:flush
In developer mode Magento recreates DI on every request—5–10x speed difference with production mode.
MySQL 8.0 — InnoDB Tuning
[mysqld]
# InnoDB buffer pool—70% server RAM
innodb_buffer_pool_size = 8G
innodb_buffer_pool_instances = 8
# Redo log
innodb_log_file_size = 1G
innodb_log_buffer_size = 64M
# Flush
innodb_flush_log_at_trx_commit = 2 # 1 = safe, 2 = faster
innodb_flush_method = O_DIRECT
# Parallelism
innodb_read_io_threads = 16
innodb_write_io_threads = 16
innodb_thread_concurrency = 0
# Queries
query_cache_type = 0 # Disable—mutex kills parallelism
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
Elasticsearch for Search
MySQL search in Magento 2 (LIKE '%query%') unacceptable for 10,000+ SKU catalogs. Elasticsearch gives full-text search with relevance, autocomplete, faceted filtering.
# Install Elasticsearch 8.x
apt install elasticsearch
# Magento configuration
bin/magento config:set catalog/search/engine elasticsearch7
bin/magento config:set catalog/search/elasticsearch7_server_hostname localhost
bin/magento config:set catalog/search/elasticsearch7_server_port 9200
bin/magento config:set catalog/search/elasticsearch7_index_prefix magento2
# Reindex
bin/magento indexer:reindex catalogsearch_fulltext
After reindex, search page with 50,000 products responds in 50–150ms instead of 2–5 seconds.
Indexing and Cron
Magento 2 has indexers recalculated on data change. For large catalogs switch to schedule mode:
# Show current mode
bin/magento indexer:show-mode
# Switch all indexers to schedule (cron update)
bin/magento indexer:set-mode schedule
# Run cron (if no system task)
bin/magento cron:install
bin/magento cron:run
Check indexer status:
bin/magento indexer:status
# catalog_category_product: Ready (last index: 1.2s)
# catalogsearch_fulltext: Ready (last index: 45.3s)
If catalog_product_price indexer runs > 5 minutes—data volume or catalog_rule problem.
Flat Catalog
For catalogs without complex attributes, Flat Tables speed queries:
bin/magento config:set catalog/frontend/flat_catalog_category 1
bin/magento config:set catalog/frontend/flat_catalog_product 1
bin/magento indexer:reindex catalog_category_flat catalog_product_flat
Flat catalog incompatible with some extensions—check after enabling.
Eliminating N+1 Problems
Diagnose via n98-magerun2:
# Install
composer global require n98/magerun2
# Profile page queries
n98-magerun2 dev:query-log:enable
# Open page in browser
n98-magerun2 dev:query-log:disable
# Analyze var/debug/db.log
grep -c "SELECT" var/debug/db.log
Typical N+1 sources in Magento 2:
-
afterLoadplugins doing queries per collection record - EAV attributes without
addAttributeToSelectin collection - blocks calling
$product->load($id)instead of collection
Correct collection loading pattern:
$collection = $this->productCollectionFactory->create();
$collection->addAttributeToSelect(['name', 'price', 'status'])
->addFieldToFilter('status', 1)
->addFieldToFilter('visibility', ['in' => [2, 3, 4]])
->setPageSize(24)
->setCurPage(1);
// One query instead of 24+1
CDN for Static Files
# Setup base URL for static via CDN
bin/magento config:set web/unsecure/base_static_url https://cdn.myshop.ru/pub/static/
bin/magento config:set web/secure/base_static_url https://cdn.myshop.ru/pub/static/
bin/magento config:set web/unsecure/base_media_url https://cdn.myshop.ru/pub/media/
bin/magento config:set web/secure/base_media_url https://cdn.myshop.ru/pub/media/
bin/magento cache:flush
Static and media served from CDN servers nearest to user—30–50% page load time reduction for remote regions.
JS/CSS Minification and Bundling
# Merging CSS/JS
bin/magento config:set dev/css/merge_css_files 1
bin/magento config:set dev/js/merge_files 1
# Minification
bin/magento config:set dev/css/minify_files 1
bin/magento config:set dev/js/minify_files 1
# JS bundling (reduces HTTP requests)
bin/magento config:set dev/js/enable_js_bundling 1
Critical JS path—requirejs-config.js and default.js load synchronously. For FCP improvement use defer for non-critical modules via custom default_head_blocks.xml.
Result Measurement
# Before optimization
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
https://myshop.ru/catalog/category/view/id/5
# After—typical result for properly configured Magento 2
# TTFB: 0.18s (page from Varnish)
# TTFB: 0.45s (PHP miss, Redis cache hit)
Timeline
PHP 8.2, OPcache, FPM pool, production mode, DI compile, static files: 1 day. MySQL tuning, Elasticsearch, flat catalog: 1–2 days. Varnish + Redis (cache + sessions): 2 days (separate pages). N+1 audit, indexers, CDN: 2–3 days. Total full optimization: 6–10 working days.







