Magento 2 Blackfire Profiling: Pages, CLI and Tests
APM tools tell you which Magento 2 request is slow. A profiler tells you why. Blackfire records every function call in a request or a CLI command and shows where time and memory go, which makes it the right tool once you know which page, API call or cron job to look at. This article covers how to set up Blackfire for Magento Open Source and Adobe Commerce 2.4.8, how to profile pages and CLI commands, and how to read the results without getting lost in a call graph with thousands of nodes.
How Blackfire works
Blackfire has three parts:
- The probe, a PHP extension. It stays idle on normal requests and only instruments a request when that request carries a signed Blackfire query. That is why it can stay installed on staging, and even production, without slowing regular traffic.
- The agent, a daemon on the server that receives profile data from the probe and sends it to Blackfire.
- The client, either the
blackfireCLI tool or the browser extension, which triggers profiles and signs the requests.
Install the agent and probe from the Blackfire package repository, configure them with your server and client credentials, and reload PHP-FPM. Then confirm the probe is loaded for both FPM and CLI:
php -m | grep -i blackfire
blackfire version
Disable Xdebug on any environment where you profile. Xdebug hooks into the same internals and distorts the timings, sometimes heavily.
Profile in production mode
Profile a store that behaves like production. In developer mode Magento generates code on the fly, compiles LESS in the browser path, skips static file signing and logs more. A profile taken in developer mode shows those costs and hides the real ones.
Before profiling:
- Switch to production mode, or at least make sure DI is compiled and static content deployed.
- Use a realistic database. A staging copy with five products will not reproduce a slow category page with 3,000.
- Decide whether you want a cold or warm cache, and be consistent.
The cache question matters. A profile with all caches warm shows the cost of a normal request. A profile right after a cache clean shows config and layout generation, which is a different problem. Both are useful, but compare like with like.
Profiling a page
The simplest way is the browser extension. Open the page, click the Blackfire icon, and it reloads the page several times and aggregates the results. For pages behind a login, such as the customer account or the cart, the browser extension is the easiest choice because it uses your session.
From the command line, blackfire curl works for public pages and API calls:
blackfire curl https://staging.example.com/women/tops.html
blackfire --samples 10 curl https://staging.example.com/women/tops.html
If Varnish or a CDN sits in front of the site, a cached page never reaches PHP and Blackfire has nothing to profile. Either profile the backend directly, add a query string that bypasses the cache, or temporarily run the profile against a host that skips Varnish. The full page cache is the fastest code path you have, and that is not what you are investigating here.
Profiling CLI commands
Many Magento performance problems live in the CLI: indexers, cron jobs, imports and queue consumers. Blackfire can wrap any PHP command:
blackfire run php bin/magento indexer:reindex catalog_category_product
blackfire run php bin/magento cron:run --group=default
For a single cron job it is often easier to call the job class directly through a small CLI command of your own, so the profile contains only that job. The custom CLI command guide shows the setup. For a long running consumer, profile a run that processes a limited number of messages, for example with the --max-messages option of queue:consumers:start.
Reading the profile
A Magento profile can contain tens of thousands of function calls. Start with the summary and work down.
Top level numbers
The profile header shows wall time, I/O wait, CPU time, memory and network. Wall time minus CPU time is roughly time spent waiting. A request with 2 seconds wall time and 300 ms CPU is waiting on something: SQL, Redis, an HTTP call, a lock. A request where CPU is close to wall time is doing too much PHP work.
The functions list
Sort by exclusive time first. Exclusive time is time spent inside the function itself, not its children. The top entries are often low level: PDOStatement::execute, curl_exec, Redis calls, or array functions. That tells you the kind of problem.
Then sort by inclusive time and look for the highest Magento or vendor function that is not framework plumbing. Something like a block's _toHtml, a plugin around getPrice, or an observer on catalog_product_load_after. That is usually the function to fix.
The call graph
The call graph highlights the hot path, the chain of calls where most time is spent. Follow it from the top. Pay attention to call counts on the edges. A method called 4,000 times on a page with 40 products means something runs 100 times per product, often a collection load or a repository call inside a loop.
SQL and HTTP tabs
The SQL view lists every query with call counts and time. In Magento, the useful pattern to look for is the same query shape with different IDs, repeated dozens or hundreds of times. That is an N+1, and the fix is to load the data once for all items, usually by adding it to the collection or using a batch loader.
The HTTP view shows outgoing requests. Any external call during a storefront page render deserves a hard look.
Common findings in Magento profiles
- Plugins on hot methods. A plugin on a method called thousands of times per page, such as product price or attribute getters, adds up quickly. Interceptor overhead is small per call, but the plugin's own code usually is not.
- Collections without limits. A block loads a full collection to count it or to pick one item.
- Config reads in loops.
ScopeConfigInterface::getValueis cached, but calling it thousands of times still costs. Read once and keep the value. - Object creation through the object manager in tight loops, especially for heavy models.
- Uncached blocks that rebuild expensive HTML on every request, even though the content changes once a day.
The plugins, observers and preferences guide explains how each extension point affects performance.
Comparing profiles
Blackfire can compare two profiles and show what changed, in time, memory and call counts. This is the proper way to check a fix. Profile before the change, apply the fix, profile again, and compare. The comparison shows whether the fix helped and whether something else got worse. It is much more reliable than reloading the page and watching the network tab.
Performance tests with .blackfire.yaml
Blackfire also supports assertions stored in the project. A .blackfire.yaml file in the project root defines tests that run against every profile matching a path:
tests:
"Category pages stay lean":
path: "/.*\\.html"
assertions:
- "metrics.sql.queries.count < 200"
- "main.wall_time < 1s"
"No outgoing HTTP calls on storefront pages":
path: "/.*"
assertions:
- "metrics.http.requests.count == 0"
The numbers in this example are placeholders. Set limits from profiles of your own store, then tighten them as you fix things. Used this way, Blackfire becomes a guard against regressions, not just a debugging tool. Check the Blackfire documentation for the metric names available on your plan.
Blackfire and New Relic together
The two tools answer different questions. New Relic runs on every request and shows trends, so you know which transaction to look at. Blackfire profiles a single request in depth, so you know what to change. A typical loop is to find the transaction in New Relic, reproduce it on staging, profile it with Blackfire, fix, and compare.
FAQ
Does the Blackfire probe slow down Magento when not profiling?
The probe only instruments requests that carry a signed Blackfire query. Normal requests run without profiling, so the overhead on regular traffic is small.
Why does Blackfire show nothing for my category page?
The page was probably served by Varnish or a CDN and never reached PHP. Profile the backend directly or bypass the full page cache for the profiled request.
Should I profile Magento in developer mode?
No. Developer mode adds code generation and other costs that do not exist in production. Profile in production mode with compiled DI and deployed static content.
Can I profile bin/magento commands with Blackfire?
Yes. Prefix the command with blackfire run, for example blackfire run php bin/magento indexer:reindex followed by the indexer code.
I am available for freelance Magento and Adobe Commerce work, including profiling and fixing slow pages. If you need help with this, get in touch.
Related articles
Slow Magento 2 Category Pages: A Profiling Checklist
A step by step checklist to fix slow Magento 2 category pages: cache hits, profiling, N+1 product loads, layered navigation, swatches, images and the database.
Magento 2 Cache Types Explained: Clean, Flush, Custom
What each Magento 2 cache type stores, the real difference between cache:clean and cache:flush, invalidated caches, and how to add your own cache type.
Magento 2 CDN: Cloudflare vs Fastly Setup Guide
Put a CDN in front of Magento 2: Fastly as an edge full page cache, Cloudflare for static and media, real client IP, purging and settings that break things.
Magento 2 Code Review Checklist for Pull Requests
A practical Magento 2 code review checklist: DI and architecture, performance traps, security and escaping, database changes, frontend, tests and deploy impact.