Profiling Laravel with Blackfire and Xdebug | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Blackfire &amp; Xdebug Profiling in Laravel: Finding Real Bottlenecks        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)

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

  #laravel   #performance   #profiling   #blackfire   #xdebug  

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

     23 Jun 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

  10 sections  

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

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Fblackfire-xdebug-profiling-in-laravel-finding-real-bottlenecks-1&text=Blackfire+%26+Xdebug+Profiling+in+Laravel%3A+Finding+Real+Bottlenecks) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Fblackfire-xdebug-profiling-in-laravel-finding-real-bottlenecks-1) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  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. 

      Q02  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. 

      Q03  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. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3](https://cdn.msaied.com/282/71a8fc3e4cf4239b1bf6d38d57e0b985.png) laravel php8.3 enums 

### Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3

Go beyond simple enum labels. Learn how to attach behaviour, implement interfaces, and use backed enums as Elo...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/typed-enums-as-first-class-domain-citizens-in-laravel-with-php-83) [ ![RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice](https://cdn.msaied.com/281/8d2ac57c0e69d3ff9f1e68faf0e4d10c.png) laravel ai pgvector 

### RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice

Build a production-ready RAG pipeline in Laravel using pgvector, OpenAI embeddings, and a clean retrieval laye...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 24 Jun 2026     4 min read  

  Read    

 ](https://msaied.com/articles/rag-in-laravel-pgvector-embeddings-and-retrieval-augmented-generation-in-practice) [ ![Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents](https://cdn.msaied.com/283/f0a6d6a6f22d9131bacb96bae1bfc10b.png) Laravel AI Agents Queues 

### Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents

Learn how to make Laravel AI agents production-ready with automatic provider failover, background queue proces...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/ship-ai-with-laravel-failover-queues-and-middleware-for-ai-agents) 

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

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
