Horizon, Telescope, Octane — what each tool does in one sentence
Beginner5 min read·eng-14-015
Concept
Horizon, Telescope, Octane — three official Laravel packages that extend the framework in specific ways. Quick one-sentence definitions:
-
Horizon: A dashboard and configuration system for Laravel's Redis queues. Supervises queue workers, provides real-time monitoring, failed job management, and throughput metrics.
-
Telescope: A debugging assistant (devtools) that captures and displays requests, queries, exceptions, logs, queue jobs, mail, notifications, cache operations, and more — all in a web UI.
-
Octane: A high-performance application server that keeps your Laravel application in memory between requests (using Swoole or RoadRunner), eliminating the bootstrap overhead of PHP-FPM.
Horizon in detail:
- Configured in
config/horizon.php— define workers, queues, connection, max processes. - Dashboard at
/horizonshows: queue throughput, processed/failed/pending jobs, worker status. - Automatic balancing: Horizon scales workers up/down based on queue backlog.
- Tags: Associate jobs with tags (e.g., user ID) to find all jobs for a specific user.
- Uses Redis sorted sets for the monitoring data.
Telescope in detail:
- Dev-only tool. Install with
--dev. Not for production (it stores everything). - Captures: HTTP requests, SQL queries (with bindings), exceptions, mail, notifications, events, cache.
storage:pruneto clean up old entries.- Authorization gate to restrict access.
Octane in detail:
- Swoole: C extension, true async I/O, coroutines, HTTP server in PHP.
- RoadRunner: Go-based worker, no C extension required.
- App boots ONCE per worker, serves N requests in memory. 2-10x throughput improvement.
- Gotcha: Stale state between requests — static properties, singletons hold state. Must be careful.
Code Example
php
<?php
// HORIZON CONFIGURATION (config/horizon.php)
return [
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['critical', 'default', 'low'],
'balance' => 'auto', // scale workers based on load
'processes' => 10,
'tries' => 3,
'maxProcesses' => 20,
],
],
],
];
// php artisan horizon — start Horizon (manages workers + monitoring)
// php artisan horizon:pause — pause all workers
// php artisan horizon:status — check if running
// Job with Horizon tag (for filtering in dashboard)
class ProcessPayment implements \Illuminate\Contracts\Queue\ShouldQueue
{
use \Illuminate\Foundation\Bus\Dispatchable, \Illuminate\Queue\InteractsWithQueue;
public function __construct(private readonly int $userId, private readonly int $orderId) {}
public function tags(): array
{
return ["user:{$this->userId}", "order:{$this->orderId}"]; // searchable in Horizon
}
public function handle(): void { /* ... */ }
}
// TELESCOPE — reads automatically (zero config in dev)
// Any request, query, exception is automatically captured
// Access: http://localhost/telescope
// Authorization:
\Laravel\Telescope\Telescope::auth(function ($request) {
return in_array($request->user()?->email, ['admin@example.com']);
});
// OCTANE — config/octane.php
return ['server' => 'swoole', 'workers' => 4, 'task_workers' => 6];
// php artisan octane:start — start the Swoole/RoadRunner server
// php artisan octane:reload — hot-reload workers without dropping connections
// Octane safe singletons (scoped to request, not app lifetime)
class OrderController extends Controller
{
public function __construct(
// Inject in constructor — Octane resolves per-request for scoped bindings
private readonly OrderService $orderService,
) {}
}
// Register scoped bindings that reset per request
// In AppServiceProvider:
$this->app->scoped(RequestContext::class, fn() => new RequestContext());
// 'scoped' = new instance per request, not per app boot