Profiling Laravel with Blackfire and Xdebug | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Blackfire &amp; Xdebug Profiling in Laravel: Finding Real Bottlenecks

 Blackfire &amp; Xdebug Profiling in Laravel: Finding Real Bottlenecks
======================================================================

 Stop guessing where your Laravel app is slow. This guide shows how to use Blackfire and Xdebug profiling together to locate real CPU, memory, and I/O bottlenecks with concrete examples.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 23 Jun 2026 · Updated 23 Jun 2026 · 4 min read

ShareCopy linkCopied

 ![Blackfire & Xdebug Profiling in Laravel: Finding Real Bottlenecks](https://cdn.msaied.com/272/169be59c0b97e15af6ff2b7ef0a964be.png) 

  On this page +1. [Stop Guessing, Start Measuring](#stop-guessing-start-measuring)
2. [Xdebug: Local Call-Graph Profiling](#xdebug-local-call-graph-profiling)
3. [What to Look For](#what-to-look-for)
4. [Blackfire: Continuous, Low-Overhead Profiling](#blackfire-continuous-low-overhead-profiling)
5. [Install the Agent and Probe](#install-the-agent-and-probe)
6. [Profile a Laravel Artisan Command](#profile-a-laravel-artisan-command)
7. [Profile an HTTP Endpoint](#profile-an-http-endpoint)
8. [Writing Blackfire Assertions in CI](#writing-blackfire-assertions-in-ci)
9. [A Real-World Workflow](#a-real-world-workflow)
10. [Key Takeaways](#key-takeaways)

 Stop Guessing, Start Measuring
------------------------------

Every senior engineer has inherited a slow Laravel app where the previous team "optimised" it by adding Redis caches in random places. Real performance work starts with a profiler, not intuition. This article covers two complementary tools: **Xdebug** for local deep-dives and **Blackfire** for continuous, low-overhead profiling in staging and CI.

---

Xdebug: Local Call-Graph Profiling
----------------------------------

Xdebug's profiler writes cachegrind files you can open in **QCacheGrind** (macOS/Linux) or **WinCacheGrind**. Enable it only when needed — it adds significant overhead.

```ini
; php.ini (local only)
xdebug.mode=profile
xdebug.output_dir=/tmp/xdebug
xdebug.profiler_output_name=cachegrind.out.%p.%r
xdebug.start_with_request=trigger

```

Trigger a profile for a single request without slowing everything else:

```bash
curl -X GET 'https://app.test/api/reports/monthly' \
  -H 'X-Xdebug-Profile: 1'

```

Or use the browser extension **Xdebug Helper** and click the profile icon.

Once you open the cachegrind file in QCacheGrind, sort by **Self Cost** (time spent in the function itself, not its callees). A common surprise: `json_encode` on a 10 000-row Eloquent collection sitting at 40 % of wall time because someone forgot `->only(['id','name'])` on the resource.

### What to Look For

- Functions with high **inclusive cost** but low **self cost** → the real work is in their children; drill down.
- Repeated calls to the same function thousands of times → classic N+1 hiding behind a helper.
- `PDOStatement::execute` appearing hundreds of times → confirm with Laravel Debugbar or Telescope.

---

Blackfire: Continuous, Low-Overhead Profiling
---------------------------------------------

Blackfire instruments PHP at the C extension level and samples, not traces, so overhead is roughly 1–3 %. It is safe to run in staging and can be gated in CI.

### Install the Agent and Probe

```bash
# On a Debian/Ubuntu staging server
curl -1sLf 'https://packages.blackfire.io/gpg.key' | gpg --dearmor > /usr/share/keyrings/blackfire.gpg
echo "deb [signed-by=/usr/share/keyrings/blackfire.gpg] http://packages.blackfire.io/debian any main" \
  > /etc/apt/sources.list.d/blackfire.list
apt-get update && apt-get install blackfire blackfire-php

```

Add credentials to your environment:

```bash
blackfire agent:config --server-id=YOUR_ID --server-token=YOUR_TOKEN

```

### Profile a Laravel Artisan Command

```bash
blackfire run php artisan reports:generate --month=2025-05

```

Blackfire returns a URL with a flame graph and a call graph. The **hot path** is highlighted automatically.

### Profile an HTTP Endpoint

```bash
blackfire curl https://staging.app.test/api/reports/monthly \
  -H 'Authorization: Bearer TOKEN'

```

### Writing Blackfire Assertions in CI

Blackfire supports `.blackfire.yaml` for performance budgets:

```yaml
# .blackfire.yaml
tests:
  "Monthly report endpoint":
    path: /api/reports/monthly
    assertions:
      - "main.wall_time < 300ms"
      - "main.peak_memory < 32mb"
      - "metrics.sql.queries.count < 10"

```

This fails the pipeline if the endpoint regresses. The SQL query count assertion is the most valuable — it catches N+1 regressions before they reach production.

---

A Real-World Workflow
---------------------

1. **Reproduce the slow request** in a local or staging environment.
2. **Xdebug profile** to get the full call graph with exact timings.
3. **Fix the obvious wins**: eager-load relations, add missing indexes, reduce serialisation payload.
4. **Blackfire profile** before and after to compare call graphs and confirm improvement.
5. **Commit a `.blackfire.yaml` assertion** so the regression cannot sneak back.

```php
// Before: N+1 inside a resource
public function toArray(Request $request): array
{
    return [
        'id'     => $this->id,
        'author' => $this->post->user->name, // two lazy loads per iteration
    ];
}

// After: eager-load in the controller
$comments = Comment::with('post.user')->paginate(50);

```

Blackfire's comparison view will show `metrics.sql.queries.count` drop from 101 to 2 — a number you can screenshot and put in the PR description.

---

Key Takeaways
-------------

- Use **Xdebug** locally for full call graphs; use **Blackfire** in staging/CI for low-overhead continuous profiling.
- Sort Xdebug call graphs by **self cost** first, then drill into high-inclusive-cost callers.
- Blackfire's **SQL query count assertion** in CI is the single most effective N+1 regression guard.
- Always profile **before and after** a fix; perceived improvements without data are just opinions.
- Keep profiling artefacts out of production — gate Xdebug behind an environment check or a trigger header.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [performance](https://www.msaied.com/public/articles?search=performance)
- [profiling](https://www.msaied.com/public/articles?search=profiling)
- [blackfire](https://www.msaied.com/public/articles?search=blackfire)
- [xdebug](https://www.msaied.com/public/articles?search=xdebug)

 Frequently asked questions 
---------------------------

  Can I run Blackfire in production Laravel apps?Blackfire's sampling overhead is low enough for staging, but most teams avoid it in production due to compliance concerns and the risk of exposing profiling endpoints. Use it in a production-mirrored staging environment instead, and enforce budgets via CI assertions.

   How do I profile a queued Laravel job with Blackfire?Wrap the job dispatch in a Blackfire CLI call: `blackfire run php artisan queue:work --once`. This profiles a single job execution and returns a call graph URL, making it easy to spot slow serialisation or database calls inside jobs.

   Xdebug profiling makes my app too slow to use locally. What can I do?Set `xdebug.start\_with\_request=trigger` so profiling only activates when you send the `X-Xdebug-Profile` header or use the browser extension. This keeps normal requests at full speed and only instruments the specific request you care about.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleLaravel Reverb at Scale: Broadcasting Architecture, Queue Pressure, and Channel Authorization](https://www.msaied.com/public/articles/laravel-reverb-at-scale-broadcasting-architecture-queue-pressure-and-channel-authorization) [Next articleLaravel API Resources: Sparse Fieldsets, Conditional Relationships, and Stable Versioning](https://www.msaied.com/public/articles/laravel-api-resources-sparse-fieldsets-conditional-relationships-and-stable-versioning)  

   On this page
-------------

1. [Stop Guessing, Start Measuring](#stop-guessing-start-measuring)
2. [Xdebug: Local Call-Graph Profiling](#xdebug-local-call-graph-profiling)
3. [What to Look For](#what-to-look-for)
4. [Blackfire: Continuous, Low-Overhead Profiling](#blackfire-continuous-low-overhead-profiling)
5. [Install the Agent and Probe](#install-the-agent-and-probe)
6. [Profile a Laravel Artisan Command](#profile-a-laravel-artisan-command)
7. [Profile an HTTP Endpoint](#profile-an-http-endpoint)
8. [Writing Blackfire Assertions in CI](#writing-blackfire-assertions-in-ci)
9. [A Real-World Workflow](#a-real-world-workflow)
10. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
