Laravel Telescope is Laravel's official debug assistant. It records every request, database query, queued job, exception, log entry, email, and cache hit your app produces, then shows them in a dashboard at /telescope. You install it with Composer, run one Artisan command and a migration, and you can see what your app is actually doing.
Most Laravel bugs aren't hard to fix. They're hard to see. A page is slow, a job silently fails, an email never arrives, and dd() only shows you one moment in time. Telescope shows you the whole request, start to finish.
This guide covers Telescope setup, the watchers worth your attention, three real debugging workflows, and how to run it safely outside your laptop.
Key Takeaways
Install in about two minutes:
composer require laravel/telescope, thenphp artisan telescope:installandphp artisan migrate.The Query watcher flags anything slower than 100 ms and groups every query under the request that ran it, which makes N+1 problems obvious.
The Job watcher links failed jobs to their exceptions, so you can see the payload and stack trace in one place.
Schedule
telescope:prunedaily. Without it, thetelescope_entriestable grows fast.Telescope is a development tool first. For production, use filters and an authorization gate, or pair it with Pulse or Nightwatch.
The developers in this article's examples are illustrative composites based on common Laravel debugging scenarios.
What Laravel Telescope Does
Telescope works through watchers. Each watcher listens to one part of your app and stores what it sees as an entry. Entries created during the same request or Artisan command are grouped into a batch, so you can open a request and see every query, cache call, event, and log line it triggered.
That grouping is the real value. A log file tells you what happened. Telescope tells you what happened together.
Telescope ships with 18 watchers. These are the ones you'll use most:
Watcher | What it records | Use it to debug |
|---|---|---|
Request | Method, URL, headers, session, response, duration | Slow pages, wrong responses |
Query | Raw SQL, bindings, execution time | Slow queries, N+1 problems |
Job | Payload, status, connection, queue | Failed or stuck jobs |
Exception | Message, stack trace, context | Errors users never report |
Log | Log messages (error level and above by default) | Hidden warnings |
Full in-browser email preview, | Emails that look wrong or never send | |
HTTP Client | Outgoing requests and responses | Third-party API failures |
Cache / Redis | Hits, misses, writes, commands | Cache that isn't caching |
Schedule | Command and output of scheduled tasks | Cron jobs that "ran" but did nothing |
The rest cover batches, Artisan commands, dumps, events, gates, models, notifications, and views. All of them are listed in the official Telescope documentation.
Laravel Telescope Setup
Step 1: Install Telescope
From your project root, run:
composer require laravel/telescope
php artisan telescope:install
php artisan migrate
telescope:install publishes the config file, the dashboard assets, and app/Providers/TelescopeServiceProvider.php. The migration creates the tables Telescope writes to. Open http://your-app.test/telescope and you'll see the dashboard.
Telescope 5.x supports Laravel 8 through 13 and PHP 8.0 and up.
Step 2: Install for local development only (recommended)
Most teams don't want Telescope loaded in production at all. Install it as a dev dependency instead:
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
Then remove TelescopeServiceProvider from bootstrap/providers.php and register it only in the local environment, inside App\Providers\AppServiceProvider:
public function register(): void
{
// Only load Telescope locally, and only if the dev package is installed
if ($this->app->environment('local') && class_exists(\Laravel\Telescope\TelescopeServiceProvider::class)) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(TelescopeServiceProvider::class);
}
}
Finally, stop Composer from auto-discovering the package in composer.json:
"extra": {
"laravel": {
"dont-discover": [
"laravel/telescope"
]
}
},
The class_exists check matters. When you deploy with composer install --no-dev, the package isn't there, and the check keeps your app from crashing.
Step 3: Tune the config
Your settings live in config/telescope.php. Three options are worth changing on day one:
// Turn recording on or off without removing the package
'enabled' => env('TELESCOPE_ENABLED', true),
// Skip noisy paths entirely
'ignore_paths' => [
'livewire*',
'nova-api*',
'pulse*',
'_boost*',
'.well-known*',
],
'watchers' => [
// Lower the slow-query threshold from the default 100 ms
Watchers\QueryWatcher::class => [
'enabled' => env('TELESCOPE_QUERY_WATCHER', true),
'slow' => 50,
],
// Record debug-level logs, not just errors
Watchers\LogWatcher::class => [
'enabled' => env('TELESCOPE_LOG_WATCHER', true),
'level' => 'debug',
],
],
Every watcher has its own environment variable, so you can switch one off in .env without touching code.
Step 4: Schedule pruning
Telescope stores everything in the telescope_entries table, and it fills up quickly. Add this to routes/console.php:
use Illuminate\Support\Facades\Schedule;
// Delete entries older than 48 hours, once a day
Schedule::command('telescope:prune --hours=48')->daily();
Without the --hours option, Telescope prunes anything older than 24 hours. To wipe everything during a debugging session, run php artisan telescope:clear.
Starting a new Laravel project? StackMivo generates a complete Laravel app from a plain-English description, ready for Telescope, Pulse, and your usual tooling. It's standard code you own.
Debugging Requests With Telescope
The Requests tab is where most debugging starts. Each row shows the method, path, status code, and duration. Click one and you get the full picture: request payload, headers, session data, the response, and tabs for every query, log, event, and cache operation the request triggered.
Finding the slow request
Sort by duration or scan for anything over a second. Then open the request and check three tabs in order:
Queries. How many ran, and how long did the slowest take?
HTTP Client. Is an outside API holding up the page?
Cache. Are you missing the cache on every request?
Priya's checkout page took 2.4 seconds on staging, but only for some customers. In Telescope, she filtered requests to /checkout and compared a fast one with a slow one. The slow requests all had a 1.9-second outgoing call to a shipping-rate API that the fast ones skipped. Users with saved addresses hit a cache, while new users didn't. She cached rates per postcode for 10 minutes, and the slow requests disappeared.
Tagging requests for easier search
Telescope tags entries automatically with model names and the authenticated user's ID. You can add your own. This example tags every request with its response status:
use Laravel\Telescope\EntryType;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
// In App\Providers\TelescopeServiceProvider::register()
Telescope::tag(function (IncomingEntry $entry) {
return $entry->type === EntryType::REQUEST
? ['status:' . $entry->content['response_status']]
: [];
});
Now you can search for status:500 and see only failed requests.
Debugging Database Queries and N+1 Problems
The Query watcher records the raw SQL, the bindings, and the execution time of every query. Anything slower than your slow threshold is tagged slow, so it stands out in the list.
Spotting an N+1 query
An N+1 problem happens when you load a list and then run one extra query per item. In Telescope, it looks like the same select repeated dozens of times with only the ID changing:
select * from `authors` where `authors`.`id` = 1 limit 1
select * from `authors` where `authors`.`id` = 2 limit 1
select * from `authors` where `authors`.`id` = 3 limit 1
-- ...47 more
The fix is usually eager loading:
// Before: 1 query for posts + 1 per post for its author
$posts = Post::latest()->take(50)->get();
// After: 2 queries total, no matter how many posts
$posts = Post::with('author')->latest()->take(50)->get();
Marcus inherited an admin dashboard that ran 312 queries on a single page load. Telescope's request view showed most of them were the same three lookups repeated for each order row. Adding with(['customer', 'items', 'shippingMethod']) dropped the page to 9 queries and cut load time from 3.1 seconds to 280 ms.
To catch N+1 problems before they reach Telescope at all, add Model::preventLazyLoading(! app()->isProduction()); to your AppServiceProvider. For more ways to write efficient queries, see our Laravel query builder tips.
Counting hydrated models
Sometimes the query count is fine, but you're loading far too many rows. Turn on the Model watcher's hydrations option to see how many Eloquent models each request built:
Watchers\ModelWatcher::class => [
'enabled' => env('TELESCOPE_MODEL_WATCHER', true),
'events' => ['eloquent.created*', 'eloquent.updated*'],
'hydrations' => true,
],
A request that hydrates 20,000 models to show a page of 25 is a clear sign you need pagination or a select with fewer columns.
Debugging Queued Jobs
Jobs are the hardest part of a Laravel app to debug because they run somewhere else, later. The Job watcher records each job's class, queue, connection, payload, and status, and it links failures to the exception that caused them.
Tracing a failed job
Open the Jobs tab and filter for failed jobs. Each entry shows:
The payload, so you can see exactly what data the job received
The exception and full stack trace
Related entries: the queries, logs, mail, and HTTP calls the job made before it failed
Elena's nightly SendInvoiceReminders job reported success but sent nothing to about 40 customers. Telescope showed the job did run. Its Mail tab, though, held only half the expected emails, and the Log tab had a warning about a missing billing_email field. Those customers had been imported from an old system with the field left empty. A fallback to the account email fixed it.
Use Telescope to see what a job did. For queue throughput, worker counts, and retries at scale, pair it with Horizon. Our guides to Laravel queues and workers and Horizon with Redis go deeper.
Using Laravel Telescope Safely Outside Local
Telescope records request bodies, headers, session data, and email content. In the wrong hands, that's a data leak. If you run it anywhere other than your laptop, apply all four of these safeguards.
1. Lock down the dashboard
By default, /telescope is open only in the local environment. Everywhere else, access is controlled by the viewTelescope gate in TelescopeServiceProvider:
use App\Models\User;
use Illuminate\Support\Facades\Gate;
protected function gate(): void
{
Gate::define('viewTelescope', function (User $user) {
// Allow only named admins. Prefer a role check in real apps.
return in_array($user->email, [
'[email protected]',
]);
});
}
Warning: Make sure APP_ENV=production is set on production servers. If it's left as local, the dashboard is publicly accessible.
2. Hide sensitive data
The generated provider already hides the CSRF token and cookie headers outside local. Add passwords, tokens, and any personal fields you collect:
protected function hideSensitiveRequestDetails(): void
{
if ($this->app->environment('local')) {
return;
}
Telescope::hideRequestParameters([
'_token',
'password',
'password_confirmation',
'card_number'
]);
Telescope::hideRequestHeaders([
'cookie',
'set-cookie',
'x-csrf-token',
'x-xsrf-token',
'authorization'
]);
}
3. Record only what matters
The default filter records everything locally, but only exceptions, failed requests, failed jobs, scheduled tasks, and monitored tags elsewhere:
Telescope::filter(function (IncomingEntry $entry) use ($isLocal) {
return $isLocal ||
$entry->isReportableException() ||
$entry->isFailedRequest() ||
$entry->isFailedJob() ||
$entry->isScheduledTask() ||
$entry->hasMonitoredTag();
});
You can add $entry->isSlowQuery() to keep slow queries too. This keeps storage small and limits what personal data Telescope holds.
4. Prune aggressively
On staging, 24 to 48 hours of history is usually enough. Run telescope:prune daily, and watch the size of telescope_entries.
Telescope vs Pulse vs Nightwatch
Laravel now has three first-party observability tools. They answer different questions.
Tool | Best for | Where it runs | Question it answers |
|---|---|---|---|
Telescope | Deep debugging | Local or staging | "What exactly happened in this request?" |
Pulse | Trends and hotspots | Self-hosted, production-safe | "Which endpoints, queries, and users are slowest this week?" |
Nightwatch | Full production monitoring | Hosted service | "Is production healthy right now, and who's affected?" |
Use Telescope while you build. Use Pulse for a lightweight production dashboard. Consider Nightwatch when you need alerting and long-term history without running it yourself. Many teams run Telescope locally and Pulse in production at the same time.
Frequently Asked Questions
What is Laravel Telescope used for?
Laravel Telescope is a debugging tool that records requests, database queries, queued jobs, exceptions, logs, mail, cache operations, and scheduled tasks. It shows them in a web dashboard so you can see everything a request or job did.
Should I use Laravel Telescope in production?
Usually not with all watchers on. If you do, restrict the dashboard with the viewTelescope gate, hide sensitive request data, filter entries to exceptions and failures, and prune daily. For ongoing production monitoring, Pulse or Nightwatch is a better fit.
How do I find N+1 queries with Telescope?
Open a request and check its Queries tab. If the same query repeats many times with only an ID changing, you have an N+1 problem. Fix it with eager loading using with().
Why is my telescope_entries table so large?
Telescope stores every entry from every watcher. Schedule php artisan telescope:prune to run daily, disable watchers you don't need, and add paths to ignore_paths.
Does Telescope slow down my app?
It adds some overhead because it records and stores data for every request. That's fine locally. In shared environments, filter what it records and turn off heavy watchers like Model and View.
Conclusion
Laravel Telescope turns guesswork into evidence. Instead of scattering dd() calls and reading log files line by line, you open a request and see every query, job, email, and exception it produced.
Start with the local-only install, lower the slow-query threshold to 50 ms, and schedule daily pruning. Use the Queries tab to hunt N+1 problems and the Jobs tab to trace failures back to their payloads. Then, as your app reaches production, add Pulse or Nightwatch for the monitoring Telescope wasn't built for.
Want to spend less time on boilerplate and more time on the interesting bugs? Build your next Laravel app with StackMivo, free with 20 credits →