Performance issues in Magento 2 are rarely obvious. A page that takes 4 seconds might be suffering from a single misconfigured plugin, a runaway EAV query, or a third-party module making 200 redundant cache reads. Without proper profiling tools, you're flying blind — guessing, commenting out code, and hoping for the best.
This guide walks through the full profiling toolkit for Magento 2: from quick built-in options to professional-grade APM tools like Blackfire and New Relic. You'll learn what to use, when to use it, and how to interpret the results.
Why Generic Profiling Isn't Enough
Magento 2 is a complex, event-driven framework with hundreds of plugins, observers, and DI-compiled classes running on every request. A standard PHP profiler shows you call stacks, but doesn't give context about why something is slow.
You need tools that understand:
- The plugin/interceptor chain
- DI container overhead
- MySQL query patterns (N+1, full scans)
- Cache hit/miss ratios
- Layout XML parsing and block rendering
Let's start simple and work up to the heavy artillery.
1. Magento's Built-in Profiler
Magento ships with a lightweight built-in profiler you can enable without any extra tooling. It's ideal for quick debugging in local and staging environments.
Enable via environment variable
# In your .htaccess or nginx config
MAGE_PROFILER=html
# Or via pub/index.php temporarily
$_SERVER['MAGE_PROFILER'] = 'html';
Or set it in app/etc/env.php:
'x-frame-options' => 'SAMEORIGIN',
'MAGE_MODE' => 'developer',
'dev' => [
'profiler' => '1'
],
Once enabled, a profiler table appears at the bottom of each page showing timer blocks, nesting levels, and call counts. Look for:
- Blocks with high "Avg" time and low call counts (expensive single operations)
- Blocks called hundreds of times (loop inefficiency or plugin waterfalls)
Custom profiler blocks in your own code
use Magento\Framework\Profiler;
Profiler::start('my_custom_operation');
// ... your code
Profiler::stop('my_custom_operation');
This is great for benchmarking specific methods during development without a full APM setup.
2. MySQL Query Logging
Slow queries are the most common bottleneck in Magento. Enable MySQL slow query logging to capture them:
-- In MySQL session or my.cnf
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
SET GLOBAL log_queries_not_using_indexes = 'ON';
Then analyze with mysqldumpslow or pt-query-digest:
# Group by query pattern, sort by total time
pt-query-digest /var/log/mysql/slow.log --limit 20
Magento's query log via Varien_Db
In developer mode, you can also log all queries Magento executes. Or use a custom plugin on Magento\Framework\DB\Adapter\Pdo\Mysql::query() to log slow queries with their origin (class + method).
Spotting N+1 patterns
The classic N+1 problem in Magento looks like this in your logs:
SELECT * FROM catalog_product_entity WHERE entity_id = 42
SELECT * FROM catalog_product_entity WHERE entity_id = 43
SELECT * FROM catalog_product_entity WHERE entity_id = 44
... (200 more)
Fix: use addAttributeToSelect('*') on collections and load them in bulk, or leverage the product repository with proper filtering.
3. Blackfire.io — The Gold Standard
Blackfire is the professional choice for PHP profiling. It integrates natively with Magento 2 and produces call graphs that reveal exactly where time and memory are spent.
Installation
# Install Blackfire PHP extension
curl -sS https://packages.blackfire.io/gpg.key | sudo apt-key add -
echo "deb http://packages.blackfire.io/debian any main" | sudo tee /etc/apt/sources.list.d/blackfire.list
sudo apt-get update && sudo apt-get install blackfire-php blackfire
# Configure credentials
blackfire agent:config
Add to your php.ini:
extension=blackfire.so
blackfire.agent_socket=tcp://127.0.0.1:8307
Profile a page
blackfire curl https://yourstore.local/catalog/category/view/id/5
Or use the browser extension for GUI-driven profiling with comparison between runs.
Reading the call graph
Blackfire generates an interactive flame graph. Key metrics to watch:
| Metric | What it means |
|---|---|
| Wall time | Real elapsed time |
| CPU time | Processing time (excludes I/O waits) |
| I/O time | Disk/network waits (usually DB or cache) |
| Memory | Peak memory per call |
| Calls | How many times a function was invoked |
Focus on nodes with high exclusive time (time spent in that function itself, not its children). A function called 1,000 times with 0.1ms each adds up to 100ms — often invisible until you see the call count.
Blackfire builds & assertions
For CI/CD pipelines, define performance budgets:
# .blackfire.yaml
tests:
"Homepage must be fast":
path: /
assertions:
- "main.wall_time < 800ms"
- "metrics.sql.queries.count < 30"
- "metrics.cache.read.count < 100"
This fails the build if the homepage regresses beyond your defined thresholds.
4. New Relic APM
New Relic is better suited for production monitoring and long-term trend analysis. Where Blackfire is surgical (profile this specific request), New Relic is epidemiological (what's slow across thousands of requests).
Install the PHP agent
curl -L https://download.newrelic.com/php_agent/release/newrelic-php5-*.tar.gz | tar -xz
sudo ./newrelic-install install
Configure in newrelic.ini:
newrelic.appname = "Magento Production"
newrelic.license = "YOUR_LICENSE_KEY"
newrelic.transaction_tracer.enabled = true
newrelic.transaction_tracer.threshold = 500ms
newrelic.slow_sql.enabled = true
What to watch in New Relic
Transaction traces — drill into the slowest requests over time. New Relic shows the full call stack including DB queries, external calls, and cache operations.
Apdex score — a customer satisfaction metric. Target > 0.9 for Magento stores. Anything below 0.7 needs immediate attention.
Database tab — shows the slowest SQL queries across all transactions, with execution plans and call frequency.
Custom attributes — add Magento-specific context:
if (extension_loaded('newrelic')) {
newrelic_add_custom_parameter('customer_group', $customerGroup);
newrelic_add_custom_parameter('store_id', $storeId);
newrelic_add_custom_parameter('cache_hit', $cacheHit ? 'yes' : 'no');
}
5. Tideways — APM Built for PHP Frameworks
Tideways is worth mentioning as a Magento-aware alternative to New Relic. It understands Magento's MVC structure and groups transactions by controller action automatically. Setup is similar to Blackfire — install the PHP extension, configure credentials, and it starts collecting data passively.
6. The Xdebug + KCachegrind Combo (Local Only)
For deep local debugging, Xdebug's profiling mode generates Cachegrind files visualizable with KCachegrind or QCachegrind:
; php.ini
xdebug.mode = profile
xdebug.output_dir = /tmp/xdebug
xdebug.profiler_output_name = cachegrind.out.%t.%p
Trigger with: ?XDEBUG_PROFILE=1 on a request URL.
Open the .cachegrind file in KCachegrind to see a full call tree with inclusive/exclusive times.
⚠️ Never enable Xdebug profiling in production — it adds massive overhead and generates large files.
Practical Debugging Workflow
Here's a proven workflow when a Magento page is suddenly slow:
- Check obvious causes first — deployment recently? New module? Config cache cleared?
- Enable MySQL slow query log and check for expensive queries
- Blackfire profile the slow page — look at exclusive times > 50ms
-
Check the plugin chain on hot paths (e.g.,
catalog_product_load_afterobservers) - Profile again with the fix — compare Blackfire runs side by side
- Add a Blackfire assertion to prevent regression
Quick wins to check first
Before deep profiling, verify these common culprits:
# Check OPcache is enabled and warm
php -r "var_dump(opcache_get_status()['opcache_enabled']);"
# Check Redis connectivity and hit ratio
redis-cli INFO stats | grep keyspace
# Check if Magento caches are enabled
bin/magento cache:status
# Check for runaway cron jobs
bin/magento cron:status
Profiling in CI/CD
Automate performance regression detection with Blackfire in your pipeline:
# GitHub Actions example
- name: Run Blackfire performance tests
run: |
blackfire curl --json https://staging.yourstore.com/ \
--assert "main.wall_time < 1s"
This prevents performance regressions from reaching production undetected — the same way unit tests prevent functional regressions.
Summary
| Tool | Best for | Environment |
|---|---|---|
| Built-in Profiler | Quick timer blocks | Dev/staging |
| MySQL slow log | Query-level analysis | All |
| Blackfire | Surgical code profiling | Dev/staging/CI |
| New Relic | Production monitoring | Production |
| Xdebug + KCachegrind | Deep call graph analysis | Dev only |
Profiling is a skill that compounds over time. The more you profile, the faster you'll spot patterns — that suspicious 300ms block in Magento\Catalog\Model\ResourceModel\Product\Collection::load, the plugin interceptor chain that fires 40 times per request, or the layout XML file that parses 200KB of XML on every uncached page load.
Start with the built-in profiler and MySQL logs. Graduate to Blackfire when you need precision. Use New Relic in production to catch what you missed. And always — always — profile before and after any optimization to prove it actually helped.









