Laravel 13 Performance & Scaling: Real-Time Without Redis, Benchmarks, and Building for the Long Game

  • Home
  • Laravel
  • Laravel 13 Performance & Scaling: Real-Time Without Redis, Benchmarks, and Building for the Long Game
Front
Back
Right
Left
Top
Bottom
TOMORROW
Every App You

Build Today Is Tomorrow's Legacy System

I know that sounds dramatic. But after almost a decade of building and maintaining web applications, I’ve seen it happen to every team that didn’t think about scale early.

The app that starts as a weekend MVP becomes the product that raises a Series A. The internal tool that handles 50 concurrent users suddenly needs to handle 5,000. The “we’ll optimize later” decision becomes a 3-month refactor that pushes your roadmap back a quarter.

Laravel 13 ships features that directly address this problem — before you hit the wall. The Reverb database driver, performance improvements from PHP 8.3, and the ecosystem tooling around benchmarking give you a solid foundation to build on. This final post in our series is about using them wisely.

SCALING

Performance & Scaling

Real-Time Without Redis: The Reverb Database Driver

Before Laravel Reverb existed, teams had two options for WebSockets: pay for Pusher (great, but expensive at scale) or self-host their own WebSocket server (painful, complex). Then Laravel Reverb arrived as a first-party, self-hosted WebSocket server.

But Reverb still required Redis for horizontal scaling — sharing WebSocket connection state across multiple server instances. For many teams, especially smaller ones, this meant provisioning a separate Redis cluster just to get real-time features working across multiple nodes.

Laravel 13 ships a native database driver for Reverb, which means teams can implement WebSocket functionality without adding Redis to their infrastructure. For smaller applications and teams that want to keep their stack lean, this removes a real dependency.

How It Works

Configure the Reverb database driver in `config/broadcasting.php`:

Copy to clipboard
// config/broadcasting.php — Laravel 13
'reverb' => [
    'driver' => 'reverb',
    'key'    => env('REVERB_APP_KEY'),
    'secret' => env('REVERB_APP_SECRET'),
    'app_id' => env('REVERB_APP_ID'),
    'options' => [
        'host'   => env('REVERB_HOST', 'localhost'),
        'port'   => env('REVERB_PORT', 8080),
        'scheme' => env('REVERB_SCHEME', 'http'),
    ],
    'scaling' => [
        'driver' => 'database', // 👈 New in Laravel 13 — no Redis needed
    ],
],
Copy to clipboard
# .env
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=your_app_id
REVERB_APP_KEY=your_app_key
REVERB_APP_SECRET=your_app_secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http

# Start the server:
# php artisan reverb:start
That’s it. No Redis. No separate message broker. Your existing MySQL or PostgreSQL database handles WebSocket connection state.
A Real-Time Notification Feature — End to End
Here’s a complete live notification flow using Reverb in Laravel 13:
Copy to clipboard
// 1. Define the event — app/Events/OrderStatusUpdated.php
use Illuminate\Broadcasting\Channel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class OrderStatusUpdated implements ShouldBroadcast
{
    public function __construct(
        public readonly Order $order,
        public readonly string $newStatus
    ) {}

    public function broadcastOn(): Channel
    {
        return new Channel('orders.' . $this->order->user_id);
    }

    public function broadcastAs(): string
    {
        return 'order.updated';
    }
}

// 2. Fire the event from your controller
public function updateStatus(Order $order, string $status): JsonResponse
{
    $order->update(['status' => $status]);
    broadcast(new OrderStatusUpdated($order, $status))->toOthers();
    return response()->json(['status' => 'updated']);
}

// 3. Listen on the frontend — Laravel Echo + Reverb
import Echo from 'laravel-echo';

const echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT,
});

echo.channel(`orders.${userId}`)
    .listen('.order.updated', (e) => {
        console.log(`Order ${e.order.id} is now: ${e.newStatus}`);
        updateOrderUI(e.order);
    });
When to Use Database vs Redis for Reverb
This is the question I get most from teams considering the upgrade:
Scenario Recommended Driver
Single server, ≤ 1,000 concurrent connections Database ✅
Multi-server deployment, moderate traffic Database ✅ (Laravel 13)
High-throughput: 10,000+ concurrent connections Redis (Redis is still recommended)
Teams wanting minimal infrastructure Database ✅
Laravel Cloud (managed) Managed Reverb clusters
For high-throughput apps with thousands of concurrent WebSocket connections, Redis remains the recommended option. The database driver is ideal for small-to-medium projects that want to minimize infrastructure complexity. A single Reverb server can handle thousands of concurrent WebSocket connections.
For business leaders
The database driver means your development team can ship real-time features (live notifications, collaborative editing, live dashboards) without provisioning, securing, and paying for a Redis cluster. For early-stage products, this is a meaningful infrastructure cost reduction.
BENCHMARK
Benchmarking Your App in Laravel 13

Performance Tuning

Why Benchmarking Matters More Than Intuition
After years of performance work, I’ve learned one uncomfortable truth: developer intuition about bottlenecks is wrong more often than right. You optimize the query you *think* is slow. The actual slow path is in the file upload middleware you wrote six months ago and forgot about. Benchmark first. Optimize based on data.
Built-In Benchmarking With `Benchmark::measure()`
Laravel has a built-in `Benchmark` facade that gives you precise timing data without external tools:
Copy to clipboard
use Illuminate\Support\Benchmark;

// Measure a single operation
$result = Benchmark::measure(fn () => User::with('orders')->get());
// Returns: [result, duration_in_ms]

// Compare multiple approaches
[$simpleTime, $eagerTime] = Benchmark::measure([
    'simple'      => fn () => User::all(),
    'with_orders' => fn () => User::with('orders')->get(),
]);

// Output: ['simple' => 12.4ms, 'with_orders' => 31.2ms]
The Performance Audit Checklist for Laravel 13

Here’s the checklist I run on every production Laravel app before any scaling conversation:

Database Query Analysis
Copy to clipboard
# Enable query logging in dev
DB::enableQueryLog();
// ... your operation
$queries = DB::getQueryLog();
// Sort by query time — find your slowest queries
Use Laravel Telescope or Laravel Pulse for visual query monitoring.
N+1 Detection
Copy to clipboard
// config/database.php — enable in development
'strict' => true, // Forces N+1 to throw exceptions in dev

// Or use Laravel Debugbar (package)
// or run: php artisan db:show for schema analysis
Eager Loading Audit
Copy to clipboard
// Bad — N+1 queries
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name; // One query per post
}

// Good — 2 queries total
$posts = Post::with('author')->get();
foreach ($posts as $post) {
    echo $post->author->name;
}
Cache Hit Rate Monitoring
Copy to clipboard
// Track cache hits vs misses with Laravel Pulse (built-in monitoring)
php artisan pulse:check

// Or manually track in your service
$cached = Cache::get($key);
if (!$cached) {
    metrics()->increment('cache.miss');
    $cached = $this->expensiveOperation();
    Cache::put($key, $cached, now()->addMinutes(15));
} else {
    metrics()->increment('cache.hit');
}
Cache Hit Rate Monitoring
For compute-intensive APIs or high-RPS endpoints, Laravel Octane (available with Laravel 13) keeps your application booted between requests:
Copy to clipboard
composer require laravel/octane
php artisan octane:install --server=frankenphp
php artisan octane:start --workers=4 --max-requests=500
Benchmarks show Laravel 13 on PHP 8.3 handles roughly 445 requests per second for standard API endpoints — and Octane can multiply this several times over for appropriate workloads.

Laravel 13 doesn’t reinvent the wheel. It makes the wheel roll better, in more terrain, with less maintenance. That’s the kind of engineering I respect.

Build it. Ship it. Measure it. Then optimize. Laravel 13 gives you the tools to do all four without changing stacks, hiring a DevOps team, or rewriting from scratch. That’s the promise of a mature framework — and Laravel 13 delivers on it.

Explore project snapshots or discuss custom web solutions.

Make it work, make it right, make it fast — in that order.

Kent Beck, Extreme Programming Explained - 2000

Thank You for Spending Your Valuable Time

I truly appreciate you taking the time to read blog. Your valuable time means a lot to me, and I hope you found the content insightful and engaging!
Front
Back
Right
Left
Top
Bottom
FAQ's

Frequently Asked Questions

Yes. The database driver is production-ready for small-to-medium applications. It uses your existing MySQL or PostgreSQL instance — the same database your app already depends on. For high-throughput apps (10,000+ concurrent connections), Redis remains the recommended scaling backend.

Laravel Pulse (first-party, free) for application metrics. Laravel Telescope for local/staging debugging. Cloudways Monitoring, Datadog, or New Relic for infrastructure-level visibility. The combination covers request performance, query analysis, and infrastructure health.

A well-tuned single server with Laravel Octane and PHP 8.3 can handle significant traffic. Consider horizontal scaling when your average response time exceeds 200ms under load, or when your database becomes the bottleneck rather than PHP. The Laravel Cloud platform handles auto-scaling transparently if you want to avoid managing this yourself.

Laravel Cloud offers fully managed WebSocket infrastructure powered by Laravel Reverb clusters. It's excellent for teams that want to minimize operations overhead. Self-hosting (via Forge + DigitalOcean/AWS) gives more control at potentially lower cost for teams with infrastructure expertise.

N+1 query prevention. It's the most common Laravel performance issue, it's easy to introduce accidentally, and fixing it with eager loading is one of the highest-ROI optimizations you can make. Use `DB::enableQueryLog()` in development to catch it. The Eloquent documentation at laravel.com/docs/13.x/eloquent-relationships covers eager loading in depth.

Comments are closed