Laravel Performance Optimization: Caching, Queues & Scaling Guide 2025

A slow Laravel app costs you users, rankings, and money. The good news is that most performance problems come from the same handful of causes — uncached bootstrapping, blocking operations in the request cycle, and unoptimised queries. This guide covers the fixes I apply to every production Laravel project.

1. Caching Strategies

Config, route, and view caching

These three Artisan commands are the easiest wins and should be part of every production deployment script. They compile your config files, route definitions, and Blade templates into single cached files so Laravel doesn't re-parse them on every request:

php artisan config:cache php artisan route:cache php artisan view:cache

Run these after every deployment. If you change a config or route file locally, clear the cache with php artisan optimize:clear before testing.

Redis for data caching

Redis is the right choice for application-level caching in production — it's in-memory, extremely fast, and supports advanced features like cache tags. Set it as your cache driver and use Cache::remember() for expensive operations:

# .env CACHE_DRIVER=redis
// Basic put/get Cache::put('key', 'value', 600); $value = Cache::get('key'); // Cache and compute in one call — the most common pattern $users = Cache::remember('users.active', 3600, function () { return User::where('active', true)->get(); });
Important: Always invalidate or update the cache when the underlying data changes. Stale cache is worse than no cache — users get wrong data and you'll spend hours debugging it.

2. Queue Optimization

Move blocking work off the request cycle

Any task that doesn't need to complete before you return a response should be a queued job — emails, notifications, report generation, image processing, third-party API calls. This is the single biggest improvement for perceived performance.

# Set queue driver QUEUE_CONNECTION=redis
# Create a job php artisan make:job SendWelcomeEmail # Dispatch it SendWelcomeEmail::dispatch($user); # Run the worker php artisan queue:work --tries=3

In production, use Supervisor to keep queue workers running and automatically restart them if they crash.

Laravel Horizon for monitoring

Horizon gives you a real-time dashboard for queue throughput, failed jobs, and worker status. It's essential once you're running queues in production:

composer require laravel/horizon php artisan horizon:install php artisan horizon

Access the dashboard at /horizon. Make sure to protect it with the gate in HorizonServiceProvider so it's not publicly accessible.

3. Scaling for High Traffic

Horizontal scaling

For high-traffic apps, you need to scale out rather than up. Run multiple Laravel app servers behind a load balancer (Nginx or AWS ELB). The key requirement is that your app must be stateless — no session or cache data stored on the local filesystem. Use Redis for both:

# .env — required for multi-server setup SESSION_DRIVER=redis CACHE_DRIVER=redis QUEUE_CONNECTION=redis

Database optimisation

Add indexes to every column used in WHERE, ORDER BY, and JOIN clauses. Fix N+1 queries with eager loading. For read-heavy applications, set up a read replica and point Laravel's read connection at it — this splits query load across two servers with almost no code changes.

# config/database.php — read/write split 'mysql' => [ 'read' => ['host' => env('DB_READ_HOST')], 'write' => ['host' => env('DB_HOST')], ... ]

CDN for static assets

Serve images, CSS, and JS through a CDN like Cloudflare or AWS CloudFront. This reduces load on your origin server and dramatically improves load times for users far from your server location. Set your ASSET_URL in .env to point at your CDN.

4. Additional Best Practices

Runtime optimisations

Enable PHP OPcache on your server — it caches compiled PHP bytecode and is one of the highest-impact single changes you can make. Always run the latest stable PHP version. Use composer install --no-dev --optimize-autoloader in production to minimise the autoloader overhead.

Common mistakes to avoid

  • Leaving APP_DEBUG=true in production — exposes stack traces and kills performance
  • Using file-based sessions or cache in a multi-server setup — requests hit different servers and lose context
  • Not running config:cache and route:cache after deployment
  • Ignoring failed queue jobs — they pile up silently and can indicate bigger problems
  • Skipping query profiling — use Laravel Debugbar or Telescope in development to catch slow queries early
Profiling tip: Don't optimise blindly. Use Debugbar or Telescope to measure first — you'll almost always find that 80% of the slowness comes from 2–3 queries or one blocking operation. Fix those before anything else.

Conclusion

Start with the quick wins — config/route/view caching and moving blocking tasks to queues. Then add Redis for data caching and fix your N+1 queries. By the time you need horizontal scaling, you'll have already eliminated most of the performance bottlenecks that would limit you anyway.

For more on database performance specifically, see the MySQL Query Optimization guide. For deployment infrastructure, see Deploying Laravel with Docker and Kubernetes.

Need Help Optimizing Your Laravel Application?

I've helped production apps handle significantly more traffic through caching, queue architecture, and infrastructure improvements. Happy to audit your stack and identify the biggest wins.

Based in Bangladesh · Remote worldwide · Fast turnaround

About the Author

Kamruzzaman Polash — Software Engineer specialising in Laravel, REST APIs, and scalable backend systems. 10+ projects delivered for clients worldwide.