Blackfire is a PHP profiler that shows not just what is slow, but why – with call graphs, timeline, and SQL query analysis. Unlike Xdebug which shows you line-level execution, Blackfire aggregates and visualises the performance bottlenecks in a way that makes them immediately actionable. I show installation in DDEV, profiling HTTP requests and CLI commands, and writing performance assertions for CI/CD.
Blackfire vs Xdebug – different tools for different jobs
| Aspect | Xdebug | Blackfire |
|---|---|---|
| Use case | Stepping through code, debugging logic | Performance profiling, finding bottlenecks |
| Overhead | Very high (2-10x slower) | Low (uses sampling) |
| Output | Breakpoint inspection, var dump | Call graph, timeline, SQL analysis |
| CI/CD integration | Not practical | Yes – performance assertions |
| Production use | No | Yes (on-demand profiling) |
Installation in DDEV
# Add Blackfire to DDEV project ddev get ddev/ddev-blackfire # Set your credentials in .ddev/.env (gitignored) BLACKFIRE_SERVER_ID=your-server-id BLACKFIRE_SERVER_TOKEN=your-server-token BLACKFIRE_CLIENT_ID=your-client-id BLACKFIRE_CLIENT_TOKEN=your-client-token # Restart to apply ddev restart # Verify Blackfire probe is loaded ddev exec php -m | grep blackfire # Output: blackfire # Check agent is running ddev exec blackfire agent:config
Profiling an HTTP request
# Profile a product page blackfire curl https://magento2-dev.ddev.site/catalog/product/view/id/42 # Profile with custom options blackfire --samples=10 curl https://magento2-dev.ddev.site/ # Profile with authentication header blackfire curl https://magento2-dev.ddev.site/rest/V1/products/MG-001 \ -H 'Authorization: Bearer YOUR_TOKEN' # The command outputs a URL to the Blackfire dashboard # where you see the full call graph and SQL queries
Profiling a CLI command
# Profile a Magento reindex ddev exec blackfire run php bin/magento indexer:reindex catalog_product_price # Profile a custom import command ddev exec blackfire run php bin/magento vendor:import:products --limit=100 # Profile with more samples for better accuracy ddev exec blackfire --samples=5 run php bin/magento catalog:product:attributes:cleanup
Understanding the Blackfire output
# Key metrics in the Blackfire dashboard: Wall Time: Total execution time including I/O waits CPU Time: Time actually using the CPU Memory: Peak memory usage SQL Queries: Count and total time of database queries HTTP Requests: Count of external HTTP calls # Call Graph interpretation: # - Width of a node = % of total time spent in that function # - Red nodes = hot paths (most time spent here) # - Click a node = see callers and callees # Common findings in Magento 2: # 1. Catalog\Model\ResourceModel\Product\Collection::load() - very wide = too many products # 2. config::get() called 500+ times - missing caching # 3. Identical SQL queries repeated N times = N+1 problem # 4. serialize()/unserialize() with large objects = expensive serialization
Performance assertions in CI/CD
# .blackfire.yaml - define performance expectations
tests:
"Product page loads fast":
path: /catalog/product/view/id/42
assertions:
- main.wall_time < 500ms
- main.peak_memory < 64mb
- metrics.sql.queries.count < 50
- metrics.http.requests.count == 0 # no external HTTP calls
"Category page":
path: /shoes
assertions:
- main.wall_time < 800ms
- metrics.sql.queries.count < 80
"Homepage":
path: /
assertions:
- main.wall_time < 300ms
- main.peak_memory < 32mb
# .github/workflows/performance.yml
name: Performance Tests
on:
pull_request:
branches: [main]
jobs:
blackfire:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Blackfire tests
uses: blackfireio/github-action@v1
with:
client-id: ${{ secrets.BLACKFIRE_CLIENT_ID }}
client-token: ${{ secrets.BLACKFIRE_CLIENT_TOKEN }}
server-id: ${{ secrets.BLACKFIRE_SERVER_ID }}
server-token: ${{ secrets.BLACKFIRE_SERVER_TOKEN }}
config: .blackfire.yaml
endpoint: https://staging.shop.example.com
Profiling programmatically in PHP
<?php
// Auto-instrumentation: add custom spans to see your code in the timeline
// Useful when you want to see exactly where within a request your code runs
use Blackfire\Client;
use Blackfire\Profile\Configuration;
class ProductImporter
{
public function import(array $products): void
{
// Instrument a specific method
$probe = \BlackfireProbe::getMainInstance();
$probe->enable();
foreach ($products as $product) {
$this->importSingle($product);
}
$probe->disable();
}
private function importSingle(array $data): void
{
// Add a custom marker visible in the Blackfire timeline
\BlackfireProbe::micro(__METHOD__ . ':start');
$this->saveToMagento($data);
\BlackfireProbe::micro(__METHOD__ . ':end');
}
}
Summary
Blackfire is the professional PHP profiling tool - it gives you actionable data where Xdebug gives you inspection capability. In DDEV the setup takes about 10 minutes. The call graph immediately shows the widest nodes (most time) and SQL query count per request. Performance assertions in CI/CD prevent performance regressions from reaching production - a product page that was 300ms should not silently become 800ms after a feature change.
