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, then php artisan telescope:install and php 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:prune daily. Without it, the telescope_entries table 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

Mail

Full in-browser email preview, .eml download

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:

  1. Queries. How many ran, and how long did the slowest take?

  2. HTTP Client. Is an outside API holding up the page?

  3. 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 →