Laravel Performance Checklist (2026): Horizon, Redis, MySQL Indexing, Caching

Enterprise Laravel performance is rarely “one fix.” It’s usually 10–20 small changes across database queries, caching, queues, infrastructure, and observability. This guide is a copy/paste checklist you can run through to cut response times, stabilize background jobs, and keep performance from degrading as the codebase grows.

Start with the full enterprise strategy first: Laravel Development (2026): The Complete Guide to Building & Scaling Enterprise Applications.



Quick Navigation


1) Baseline first (so you don’t “optimize blind”)

Before changing anything, capture a baseline. Without this, teams ship “optimizations” that don’t move the needle—or worse, regress performance.

  • Top 20 slow endpoints (p95, p99 latency)
  • Top 20 slow SQL queries (time + frequency)
  • Queue throughput (jobs/min, failed jobs, retry counts)
  • Infrastructure signals: CPU, RAM, disk I/O, DB connections
  • Error rate (timeouts, 500s, DB deadlocks)

Pro tip: Performance work is easiest when your app has basic observability. If you don’t have it, add it first. That’s what a good Laravel Maintenance plan typically formalizes (monitoring, alerts, tuning, and ongoing improvements).


2) Database: indexing + query patterns (80% of Laravel “slowness”)

Most enterprise Laravel performance issues come from database patterns: missing indexes, N+1 queries, unbounded scans, and “convenient” ORM queries that explode under real data volume.

2.1 Indexing rules (simple and brutal)

  • Index the WHERE columns you filter on frequently.
  • Index the ORDER BY columns for large datasets (especially created_at, status, tenant_id).
  • Composite indexes should match your most common query pattern (left-to-right matters).
  • Avoid SELECT * on large tables. Fetch only what you need.
  • Pagination must be bounded (cursor pagination for big tables).

2.2 The “EXPLAIN discipline” (do this for every slow query)

When a query is slow, don’t guess. Run EXPLAIN (or EXPLAIN ANALYZE where available) and fix the actual bottleneck:

  • type = ALL → full scan (you likely need an index).
  • rows is huge → your filter is not selective enough or index not used.
  • Using filesort / Using temporary → check ORDER BY + GROUP BY index support.

2.3 Kill N+1 queries in Laravel (fastest win)

Symptoms: pages become slower as the number of records grows. Fix: eager-load relationships, and avoid loading massive graphs by default.

// Bad: N+1
$orders = Order::latest()->take(50)->get();
foreach ($orders as $order) {
    echo $order->customer->name;
}

// Good: eager loading
$orders = Order::with('customer:id,name')
    ->latest()
    ->take(50)
    ->get();

2.4 High-volume tables need “hot path” patterns

  • Use covering indexes for frequently-used list views.
  • Move heavy reporting queries to read replicas (if applicable).
  • Use summary tables for dashboards (pre-aggregations), not real-time group-bys on huge tables.

If you’re seeing 20–40+ second reports in production, it’s usually time to restructure the reporting path—our Laravel Development team often implements summary models + background refresh jobs for these.


3) Laravel-level performance (cheap wins)

These are “enterprise defaults.” They don’t solve bad queries, but they remove framework overhead and stabilize performance.

  • Enable OPcache in PHP (properly configured; avoid tiny memory allocations).
  • Cache configuration: php artisan config:cache
  • Cache routes (when compatible): php artisan route:cache
  • Optimize autoload: composer install --optimize-autoloader --no-dev
  • Use queue for slow work (emails, exports, webhooks, heavy compute)

Enterprise rule: Your HTTP request should do the minimum required to respond. Everything else goes to queues.


4) Caching strategy that actually scales

Enterprise caching fails when it’s random. Use caching where it has predictable impact: repeated reads, expensive computations, and slow integrations.

4.1 The 4 cache buckets (use this mental model)

Cache typeWhat to cacheTTL guidance
Reference dataCountries, plans, static settings1–24 hours
Per-tenant summariesDashboards, counts, totals30–300 seconds
Expensive queriesComplex list filters, reports60–600 seconds + invalidation
External calls3rd-party API responsesShort TTL + fallback

4.2 Cache invalidation: pick one pattern (don’t mix everything)

  • Time-based TTL (simplest; good for dashboards)
  • Tag-based invalidation (advanced; great per-tenant)
  • Event-based invalidation (emit event on change; clear relevant keys)
// Example: per-tenant dashboard cache key
$key = "tenant:{$tenantId}:dashboard:v1";

$data = Cache::remember($key, now()->addMinutes(2), function () use ($tenantId) {
    return DashboardService::build($tenantId);
});

For enterprise clients, we usually pair caching with maintenance SLAs to keep it healthy long-term (cache key hygiene, eviction tuning, invalidation rules). See Laravel Maintenance.


5) Redis: stability + tuning (don’t treat it like a magic box)

Redis powers caching and queues in many Laravel enterprise stacks. When Redis is misconfigured, it causes unpredictable latency and queue failures.

  • Memory policy: set an eviction policy appropriate for your caching strategy.
  • Persistence: choose RDB/AOF based on durability needs (queues often require reliability).
  • Separate concerns: consider separate Redis instances/dbs for cache vs queues at scale.
  • Connection limits: avoid exploding connections (workers + web + cron).

Enterprise shortcut: If queue reliability matters, don’t let cache evictions destabilize queues. Separate them.


6) Queues/Horizon: throughput without chaos

Queues are your scaling lever. But enterprise queues need workload separation, proper timeouts, retry/backoff, and idempotency.

6.1 Separate queues by workload (non-negotiable)

  • default: quick jobs only (sub-1s tasks)
  • imports: CSV parsing, normalization, chunk processing
  • compute: rating, heavy calculations
  • notifications: email/SMS/webhooks
  • reports: exports, PDFs, scheduled reporting

6.2 Timeouts + retry_after must match reality

If your worker timeout is 60 seconds but your job takes 4 minutes, you’ll get duplicate processing, retries, and “ghost failures.” Set these intentionally.

6.3 Idempotency: the #1 enterprise queue rule

Jobs will run more than once (retries, worker restarts, network blips). Your code must be safe under duplication.

// Pattern: lock by unique key (invoice:send:{id})
$key = "invoice-email:{$invoiceId}";

Cache::lock($key, 120)->block(1, function () use ($invoiceId) {
    // Send email once
});

If you need a team to harden queue pipelines end-to-end (Horizon tuning, retries, locks, and throughput), that’s typically part of Laravel Maintenance or an architecture engagement under Laravel Development.


7) Nginx + PHP-FPM essentials (don’t ignore infrastructure)

Even perfect code can feel slow on a poorly tuned stack. These are the common enterprise “musts”:

  • HTTP keep-alive enabled (reduces handshake overhead)
  • Gzip/Brotli (compress JSON responses)
  • Correct PHP-FPM process counts (match RAM/CPU reality)
  • Real client IP headers correct (for rate limits/logs)
  • Timeouts aligned across Nginx, PHP-FPM, and load balancer

Enterprise warning: Increasing PHP-FPM workers without enough RAM causes swapping and makes everything slower. Always tune with real memory constraints.


8) Deployment & rollback performance hygiene

Many performance issues show up right after deploys: cache cold starts, missing config cache, heavy migrations, and queue restarts. Fix this with a predictable release process.

  • Warm critical caches after deploy (top endpoints, reference data).
  • Use safe migrations (avoid locking huge tables during peak traffic).
  • Restart workers in a controlled way (avoid processing duplication).
  • Monitor p95 latency and error rate for 30–60 minutes after release.

9) Enterprise Laravel Performance Checklist (Copy/Paste)

  1. Baseline: p95/p99 endpoints, slow queries, queue throughput, infra metrics.
  2. DB: fix top slow queries with EXPLAIN + indexes; kill N+1; limit SELECT fields.
  3. Pagination: use cursor pagination for big tables; avoid deep offset pages.
  4. Caching: define cache buckets + TTLs; standardize keys; add invalidation pattern.
  5. Redis: separate cache vs queue if reliability matters; tune memory + persistence.
  6. Queues: separate workloads; set realistic timeouts/retry_after; enforce idempotency.
  7. Laravel: config cache, route cache (when possible), opcache, optimized autoload.
  8. Infra: align Nginx/PHP-FPM timeouts; avoid swap; tune worker counts to RAM.
  9. Deploy: warm caches; safe migrations; controlled worker restarts; post-deploy monitoring.

Recommended Next Steps (Internal Links)

Need performance + SLA?

We can monitor, optimize, and harden your Laravel app continuously (speed, uptime, security, queues, DB).

Need architecture help?

We design scalable Laravel systems (DDD-inspired modules, event-driven flows, Horizon tuning).


FAQ

What’s the fastest performance win in most Laravel enterprise apps?

Fixing slow queries and eliminating N+1 patterns. Most “Laravel is slow” issues are actually database query issues under real data volume.

Should we cache everything?

No. Cache repeatable reads and expensive computations, and use sane TTLs. Random caching without invalidation strategy creates stale data and unpredictable bugs.

How do we keep queues stable under load?

Separate queues by workload, set realistic timeouts/retry policies, and enforce idempotency. Stability comes from predictable queue design, not just “more workers.”

When should we consider a bigger architecture change?

If you’ve fixed DB queries, caching, and queues—and still hit limits—then consider summary tables, read replicas, or modularizing hotspots. Avoid premature microservices.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *