[ GUIDE ]

Laravel p95 latency — measure it, alert on it, fix it

Why average latency is a lie, what p95 actually tells you, and how to compute it in Laravel with or without an APM.

QUICK ANSWER

What is p95 latency in Laravel and how do I measure it?

P95 latency means 95% of requests complete in this time or faster — the slowest 5% take longer. It's the industry-standard way to describe response-time health without letting outliers dominate. Compute it by recording every request's duration, sorting ascending, and taking the value at the 95th-percentile index. Laravel's nightwatch package records per-request duration; tools like NightOwl compute p95 automatically per route with trending over time.

Updated · 2026-04-13

Why average is misleading

Consider two routes with the same average latency of 149ms:

Route Avg p95 p99 Max
/api/orders149ms180ms210ms280ms
/api/checkout149ms50ms6,500ms12,000ms

Same average. Wildly different user experience. /api/orders is uniformly slow-ish; /api/checkout is fast for most people but catastrophic for a long tail. Only p95 and p99 reveal the truth.

How to compute p95 in Laravel

The exact definition: sort all N durations ascending, pick the value at index floor(N * 0.95).

Exact p95 in PostgreSQL

sql
SELECT
  route,
  COUNT(*) AS requests,
  AVG(duration_ms) AS avg_ms,
  percentile_cont(0.5) WITHIN GROUP (ORDER BY duration_ms) AS p50,
  percentile_cont(0.95) WITHIN GROUP (ORDER BY duration_ms) AS p95,
  percentile_cont(0.99) WITHIN GROUP (ORDER BY duration_ms) AS p99
FROM request_logs
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY route
ORDER BY p95 DESC
LIMIT 20;

percentile_cont computes the exact percentile by interpolation. For MySQL, PERCENTILE_CONT is window-function form (8.0+) or compute manually with ORDER BY duration_ms LIMIT 1 OFFSET FLOOR(COUNT*0.95).

How APMs compute p95 at scale

Storing every request's duration forever is expensive. APMs use streaming percentile algorithms — t-digest or HDR histograms — that estimate p95 from a small fixed memory footprint with bounded error.

NightOwl stores exact per-request duration in your PostgreSQL (which is cheap at Laravel volumes — Postgres handles this at billions of rows) and computes p95 on-demand using percentile_cont. No accuracy tradeoff.

Alerting on p95

Good p95 alerts have three properties:

  1. Per-route, not global. Global p95 hides per-endpoint regressions. Alert on specific high-value routes (/checkout, /login, /api/orders).
  2. Comparative, not absolute. "p95 exceeded 500ms" is a weak signal if the baseline was 450ms. "p95 is 30% above last week's median" catches regressions.
  3. Sustained, not instant. Alert when p95 exceeds threshold for 5+ consecutive minutes. Single-minute spikes are usually noise.

What p95 doesn't tell you

P95 is a single number — it can't tell you why the slow tail is slow. Pair it with:

  • Per-request trace view — see which query or external call ate the budget
  • User dimension — is the slow tail concentrated in specific user segments?
  • Time dimension — is the tail uniform or spiky at certain hours?
  • p99 and max — does p99 correlate with p95 or does it blow up independently?

THE EASY WAY

NightOwl computes p95 per route with trace-level drilldown

Every Laravel request is recorded with route, duration, and component spans. The requests dashboard shows p95 per route with time-series trending. Click into a route to see its slowest individual requests, and into a request to see which query or external call drove the latency.

bash
composer require nightowl/agent
php artisan nightowl:install

From $5/month flat. Data in your PostgreSQL.

Frequently asked questions

What is p95 latency?

P95 latency is the 95th percentile of response times — 95% of requests complete in this time or less, and the slowest 5% take longer. If a route has a p95 of 400ms over the last hour, that means 19 out of every 20 requests were at or below 400ms. P95 is the industry-standard way to describe 'the slow tail' without letting extreme outliers dominate.

Why use p95 instead of average latency?

Average hides the slow tail. A route that serves 99 requests in 50ms and one request in 10 seconds has an average of ~149ms — looks acceptable. Its p95 is 50ms and p99 is 10s, which correctly tells you 'most requests are fast but 1% are catastrophic.' Users experience the tail, not the average.

How do I calculate p95 latency in Laravel?

Collect every request's duration for a time window, sort them ascending, and pick the value at index floor(0.95 * count). That's the exact p95. In SQL: ORDER BY duration ASC OFFSET (COUNT * 0.95) LIMIT 1. Streaming approximations (t-digest, HDR histograms) let you compute p95 without storing every sample — what most APMs use at scale.

What's a good p95 latency for a Laravel endpoint?

Depends on the endpoint class. UI-blocking API endpoints: under 200ms p95 is good, 200-500ms is acceptable, above 500ms is a UX problem. Full page renders: 500-1000ms at p95 is acceptable. Background jobs and reports: seconds-to-minutes are fine if they're not blocking users. Set per-endpoint SLOs instead of a global threshold.

P95 vs p99 latency — which should I track?

Both, for different questions. P95 is the 'typical bad case' — covers most user pain and is stable enough to trigger alerts on. P99 is the 'rare bad case' — extremely slow outliers that might indicate specific user segments or data patterns hitting worst-case paths. Alert on p95 for day-to-day monitoring; investigate p99 when you're chasing long-tail bugs.

How do I monitor p95 latency trends in Laravel?

You need per-minute or per-hour buckets with p95 computed inside each bucket. Bucket size depends on traffic — high-traffic routes can use 1-minute buckets; low-traffic routes need 15-minute or hourly buckets to have enough samples. Plotting p95 over time shows regressions (a deploy made it worse) and trends (a route is slowly degrading as data grows).

Can I compute p95 from Laravel logs?

Yes, if your logs record per-request duration. Parse the log, sort by duration within a time window, pick the 95th-percentile value. Painful to do continuously — you'd be re-parsing logs. APMs compute p95 at ingest time with streaming algorithms. If you want p95 without an APM, log to a database and query with PostgreSQL's percentile_cont or MySQL's equivalent.

Why does p95 latency spike on deploy?

Usually one of: (1) opcache cold-start on first requests to new code, (2) autoload cache rebuilding, (3) warmed-cache misses if you deploy-clear caches, (4) new migrations running on the first request, (5) connection pool re-establishment. Warm the app before routing traffic (health check + pre-warming script), and use php artisan optimize after deploy to prebuild caches.

PRICING

Flat pricing. No event caps. No per-seat fees.

14-day free trial, no credit card. Your PostgreSQL, your data.

HOBBY

$5 /month

1 app · 14 days lookback · all Laravel events

TEAM

$15 /month

Up to 3 connected apps · unlimited environments · all Laravel events

AGENCY

$69 /month

Unlimited apps · unlimited agent instances · same flat rate at any traffic

Related