Laravel Pulse is a free, first-party package that turns your own application into a performance dashboard, tracking slow requests, slow queries, slow jobs, queue throughput, exceptions, cache hit rates, and server load, with no external agent and no monthly bill. You install it with Composer, run one migration, and open /pulse.

Most teams learn about a slow endpoint from a customer, not from a chart. You already know monitoring matters, and you have probably also opened a quote for a per host APM plan across four servers, then quietly closed the tab. Laravel Pulse fills that gap for the common case, because the data lives in your database and the dashboard runs inside the app you already deploy.

This guide covers what Pulse records, how to install and secure it, how to keep the write load sane under real traffic, how to add your own business metrics, and the point where a paid tool is genuinely the right call. Every command here runs against a standard Laravel install.

Key TakeawaysLaravel Pulse is free and MIT licensed, and it stores metrics in your own database rather than a vendor cloud.Out of the box you get nine cards, including slow requests, slow queries, slow jobs, queue throughput, exceptions, cache hits, and per server CPU, memory, and disk.The dashboard stays closed outside the local environment until you define a viewPulse gate, so authorization is a deliberate step rather than a default.On busy apps, switch the ingest driver to Redis, run php artisan pulse:work, and sample the high volume recorders to keep write cost low.Pulse shows aggregates, not stack traces, alerts, or distributed traces, so it complements an error tracker instead of replacing one.

What Laravel Pulse measures out of the box

Pulse ships with a set of recorders that hook into framework events and write aggregated entries to three tables, pulse_values, pulse_entries, and pulse_aggregates. Each recorder feeds a card on the dashboard, and you enable or disable them individually in config/pulse.php.

  • Servers, CPU, memory, and disk for every machine running the check daemon.

  • Application Usage, which users generate the most requests and jobs, and who hits the slowest endpoints.

  • Slow Requests, routes that cross a millisecond threshold, 1,000ms by default.

  • Slow Queries, SQL over the threshold, optionally with the file and line that issued it.

  • Slow Jobs, queued jobs over the threshold, grouped by class.

  • Slow Outgoing Requests, third party calls made through the Laravel HTTP client.

  • Queues, queued, processing, processed, released, and failed counts over time.

  • Exceptions, exception classes ranked by frequency and recency.

  • Cache, hit and miss rates globally and per key.

That list covers the questions a Laravel team asks most weeks. Which page got slow after the last deploy. Which job is clogging the default queue. Which query the new report screen fires on every page load. You can answer all three without exporting a single log line.

Instrumenting an app you have not built yet is the wrong order, of course. If you are still assembling the models, migrations, controllers, and tests, you can Try StackMivo Now to generate production-ready Laravel apps from a plain English description, then point Pulse at the result.

How to install Laravel Pulse and lock down the dashboard

Installation is three commands. Pulse needs PHP 8.2 or newer and a supported database, so check the version matrix in the official Laravel Pulse documentation before you add it to an older project. Teams still on Laravel 9 need to upgrade first.

composer require laravel/pulse
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrate

The publish step drops config/pulse.php and the Pulse migrations into your project. The migrate step creates the three tables. Visit /pulse locally and the cards start filling as soon as traffic flows through the app.

Authorize the dashboard before you deploy

Outside the local environment the dashboard is closed by default. Define a viewPulse gate in app/Providers/AppServiceProvider.php and decide exactly who gets in.

use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::define('viewPulse', function (User $user) {
 return $user->isAdmin();
});

Do not skip this step. The Pulse dashboard exposes user IDs, route names, and raw slow queries, which is precisely the shape of data you do not want public.

Run the check daemon for server metrics

The Servers card needs a long running process on each machine, started with php artisan pulse:check. Supervise it the way you already supervise queue workers, following the Supervisor pattern in the Laravel queue documentation. Give each machine a distinct PULSE_SERVER_NAME so the card does not merge two boxes into one line.

How to read the Pulse dashboard without guessing

The Pulse dashboard defaults to a one hour window, and every card respects the period selector at the top. Switch it to 24 hours or seven days when you are hunting a pattern rather than a fire.

Start with Slow Queries. It carries the highest signal in most Laravel apps, because the slow request and slow job cards usually point back at the same SQL. Keep the location option enabled in config/pulse.php so each entry records the file and line that issued the query, which turns a vague database complaint into a specific line of code.

A four person team running a logistics SaaS installed Pulse on a Friday and left it alone. By Monday the Slow Queries card showed one statement at the top with a count in the thousands, a lookup on a shipments table filtered by status and customer. The location pointed at a single Eloquent scope used by the customer portal. The table had grown past several million rows, and the composite index that query needed had never existed, because the scope was written when the table held a few thousand records. One migration later the endpoint stopped appearing in Slow Requests. Nobody opened a log file.

Read the other cards with the same habit. The Queues card tells you whether throughput keeps up or whether the released count is climbing, which usually means a job is failing and retrying. Exceptions ranks by count and recency, so a class that is both frequent and recent is your next ticket. Cache is the card people ignore, and it is where you find the key that misses on every single request. Once you know which query is the problem, the fix is a refactor, and the tooling round up on AI refactoring tools for PHP and Laravel covers the options for cleaning it up.

Tuning Laravel Pulse for production traffic

The default setup writes Pulse entries to your application database after the response is sent. That is fine for a modest app and wrong for a busy one, because every request adds writes to the same database the request just finished reading from.

Move ingest to Redis

Point the ingest driver at Redis and Pulse buffers entries into a stream instead of writing them inline. A separate worker drains that stream into storage on its own schedule.

PULSE_INGEST_DRIVER=redis
PULSE_DB_CONNECTION=pulse
PULSE_TRIM_KEEP="14 days"
PULSE_SLOW_REQUESTS_THRESHOLD=750
php artisan pulse:work

Sample the noisy recorders

Every recorder supports a sample rate. Set the user request recorder to 0.1 and Pulse stores one entry in 10, then scales the figures on the dashboard and marks them with a tilde so you know the number is an estimate. Sampling is per recorder, so you can keep exceptions at full fidelity while thinning out high volume request data.

Control retention and storage

Pulse trims old entries automatically and keeps seven days by default. Raise or lower that with PULSE_TRIM_KEEP. If your primary database is already under pressure, give Pulse its own connection through PULSE_DB_CONNECTION so monitoring writes never compete with customer traffic. Setting PULSE_ENABLED=false turns recording off entirely, which is handy in CI.

A solo developer running a side project on one small server had the opposite problem. Their app served a few thousand requests a day, so default storage ingest was never the bottleneck. What Pulse surfaced was a nightly import job holding the default queue for 20 minutes, which delayed every password reset email sitting behind it. The Queues card made the shape obvious at a glance. Moving the import onto its own queue and worker fixed a complaint the developer had blamed on the mail provider for months.

Recording your own metrics with custom Pulse cards

Framework metrics tell half the story. Pulse exposes the same recording API it uses internally, so you can track the numbers your business actually cares about. Call Pulse::record() where the event happens and chain the aggregates you want.

use Laravel\Pulse\Facades\Pulse;

Pulse::record('order_placed', $order->team_id, $order->total)
 ->sum()
 ->count();

Then build the card. A Pulse card is a Livewire component that extends the package base class and pulls those aggregates back out.

namespace App\Livewire\Pulse;

use Laravel\Pulse\Livewire\Card;
use Livewire\Attributes\Lazy;

#[Lazy]
class TopTeams extends Card
{
 public function render()
 {
 return view('livewire.pulse.top-teams', [
 'teams' => $this->aggregate('order_placed', ['sum', 'count']),
 ]);
 }
}

Publish the dashboard view with php artisan vendor:publish --tag=pulse-dashboard, then place your card in the grid beside the built in ones and set how many columns it spans.

<x-pulse>
 <livewire:pulse.servers cols="full" />
 <livewire:pulse.slow-queries cols="8" />
 <livewire:pulse.top-teams cols="4" />
</x-pulse>

This is the point where Pulse stops being a monitoring tool and starts being a small internal dashboard. Signups per hour, failed payments, and jobs per tenant, sitting next to CPU and queue depth. When that grows into a real reporting surface, the analytics dashboard use case covers building the larger version with charts, KPIs, and scheduled reports.

Where Laravel Pulse stops and paid monitoring starts

Honest answer first. Laravel Pulse is not an APM and it is not an error tracker. It is a live aggregate view of one application, and the narrow scope is deliberate.

What you need

Laravel Pulse

Hosted APM or error tracker

Cost

Free and MIT licensed, you pay only for the storage it uses

Recurring, usually per host, per seat, or per event volume

Where data lives

Your database, under your control

Vendor cloud, subject to their retention and region rules

Retention

Days by default, tunable, bounded by your own storage

Weeks or months depending on plan

Alerting

None built in, you add your own checks

Thresholds, on call routing, and integrations

Exception detail

Class, count, and recency

Full stack traces, request context, and release tracking

Tracing

Aggregate cards only

Per request traces across services

Availability in an outage

Down with your app

Independent of your app

That last row matters more than the price. If the database is down, the Pulse dashboard is down with it, so Pulse cannot page you about the incident it exists to observe. Pair it with an external uptime check, and add a dedicated service such as Sentry, Laravel Nightwatch, New Relic, or Datadog once exception volume and on call duty justify the spend.

A sensible progression for most teams looks like this. Start with Pulse on day one, because it costs nothing and answers the weekly questions. Add an error tracker when you have paying customers. Add full APM when you run multiple services and a real rotation. Buying all three before you have traffic is how monitoring budgets get spent on dashboards nobody opens.

Start with Laravel Pulse and add tools when it hurts

Laravel Pulse earns its place because setup costs almost nothing and the payoff arrives the same day. Three commands install it. One gate secures it. Nine cards answer most of the performance questions a Laravel team asks in a given week, and the data never leaves your infrastructure.

Keep four things in mind as you roll it out. Define the viewPulse gate before the first deploy. Move to Redis ingest and sampling before traffic makes the write load matter. Set retention deliberately instead of accepting the default. Treat Pulse as a complement to an error tracker, never a substitute for one.

The next step is small. Install it on one application this week, let it run for seven days, and read the Slow Queries card. Most teams find at least one missing index, one N+1 query, or one job on the wrong queue in that first week. Fix those and the dashboard has already paid for itself, without an invoice. Then build the next feature with Laravel-native intelligence behind you and watch how it behaves under real traffic.