log in
consulting hosting industries the daily tools about contact

Sentry: Error Monitoring That Doesn't Cost More Than Your Hosting

Sentry is the first error monitoring tool I've kept running on every project. Here's how I use it and what to watch out for before your bill surprises you.

I've tried a lot of error monitoring tools over the years, and most of them fall into one of two camps: too cheap to be useful, or priced like they're monitoring a Fortune 100 trading platform. Sentry sits in a genuinely useful middle ground — but only if you configure it correctly from day one, because the defaults will quietly eat your event quota and eventually your budget.

What Problem Sentry Actually Solves

Every app you ship has errors happening in production right now that you don't know about. Not the errors that blow up in a user's face and they call you — the silent ones. The job that fails at 2am. The malformed webhook from a payment processor that gets swallowed by a try/catch someone wrote in 2019. The edge-case validation failure that only hits one out of a thousand form submissions.

Sentry captures those. It groups repeated errors so you see "this happened 847 times today" instead of 847 separate emails. It attaches stack traces, request context, user info if you wire it up, breadcrumbs of what happened before the error — the stuff you actually need to diagnose something without spending three hours grepping logs.

I integrated it for a healthcare client running a patient intake portal a few years back. They had a Laravel queue worker processing HL7 messages from an EHR, and it had been silently failing on a specific message type for probably two months. Nobody knew. The EHR vendor had quietly changed a field format. Sentry found it on day one of the integration. That's the pitch.

Getting It Running in Laravel

Install the SDK:

composer require sentry/sentry-laravel

Publish the config:

php artisan sentry:publish --dsn=https://your-dsn@sentry.io/your-project-id

That drops a config/sentry.php and adds SENTRY_LARAVEL_DSN to your .env. The default config is fine to start, but you'll want to tune it. Here's what my production config typically looks like:

// config/sentry.php
return [
    'dsn' => env('SENTRY_LARAVEL_DSN'),

    'release' => env('SENTRY_RELEASE', null),

    'environment' => env('APP_ENV', 'production'),

    'breadcrumbs' => [
        'logs' => true,
        'queue_info' => true,
        'command_info' => true,
        'http_client_requests' => true,
    ],

    'tracing' => [
        'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', false),
        'queue_jobs' => true,
        'sql_queries' => true,
        'sql_bindings' => false, // keep false unless you're sure no PII in query params
        'views' => false,
    ],

    'send_default_pii' => false,

    'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE', 0.0),

    'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE', 0.0),

    'ignore_exceptions' => [
        \Illuminate\Auth\AuthenticationException::class,
        \Illuminate\Auth\Access\AuthorizationException::class,
        \Illuminate\Database\Eloquent\ModelNotFoundException::class,
        \Illuminate\Session\TokenMismatchException::class,
        \Illuminate\Validation\ValidationException::class,
        \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class,
        \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException::class,
    ],
];

That ignore_exceptions list is not optional. Leave it out and Sentry will gleefully report every 404, every CSRF mismatch, every failed login attempt, every validation error — all noise, all burning your event quota.

In bootstrap/app.php (Laravel 11) or app/Exceptions/Handler.php (Laravel 10 and below), make sure the integration is wired in. The published config handles most of this, but confirm your handler is calling \Sentry\Laravel\Integration::handles($exception) if you've customized your exception handler at all.

For queue jobs, I also manually capture context when something goes sideways:

try {
    $this->processInboundMessage($message);
} catch (\Throwable $e) {
    \Sentry\withScope(function (\Sentry\State\Scope $scope) use ($message, $e): void {
        $scope->setContext('message', [
            'id' => $message->id,
            'type' => $message->type,
            'source' => $message->source_system,
        ]);
        $scope->setTag('job.type', 'inbound_message');
        \Sentry\captureException($e);
    });

    throw $e; // still rethrow so the job fails properly
}

That context shows up in the Sentry UI alongside the stack trace. When I'm looking at an error at 6am I want to know exactly which message ID and which source system triggered it, not just the line number.

The Gotchas That Will Bite You

Performance tracing will bankrupt you if you turn it on carelessly. The traces_sample_rate defaults to 0.0 in the Laravel package but I've seen other tutorials set it to 1.0 — meaning capture a trace for 100% of requests. On any app with real traffic that's a massive volume of events. Sentry charges for transactions separately from errors. I keep it at 0.0 for most client sites, or 0.05 (5%) if they're paying for the Performance plan and actually want it.

The free tier (5,000 errors/month) disappears fast. If you're running a multi-tenant app with 50 clients and one of them triggers a bug loop, you'll burn the month's quota in an hour. Set inbound filters in the Sentry project settings: filter out known bot user agents, filter out specific URLs, and absolutely set a rate limit per issue. I use 100 events per issue per hour as a ceiling — after that Sentry stops counting new occurrences of the same error until the window resets. You still know the error exists; you're just not paying to hear about it 10,000 times.

Source maps / release tracking takes ten minutes to set up and saves hours later. Without releases, your stack traces in minified JS are useless. This isn't PHP-specific but if you're running any Vite/Laravel Mix frontend, wire up the Sentry Vite plugin and set SENTRY_RELEASE to your git SHA at deploy time. The difference between a minified trace and a readable one is enormous.

send_default_pii — leave it false. Sentry will attach IP addresses, cookies, and HTTP headers if you enable this. For healthcare or any app touching personal data that's a compliance problem waiting to happen. You can still attach specific non-PII context manually with setContext() as shown above.

Self-hosted is an option but it's a real operational burden. Sentry publishes a Docker Compose setup. I tried running it for a client who was adamant about data residency. It works, but you're now in the business of maintaining a fairly complex stack — Postgres, Redis, Kafka, Clickhouse, a handful of services — and upgrading it is not a five-minute job. For most clients the hosted tier is the right call unless you have genuine compliance or cost reasons to self-host.

When I'd Reach for This

Every production Laravel app I ship gets Sentry. No exceptions. The free tier is enough for small apps. The $26/month Developer plan covers most small business clients. It's the first thing I set up after deploying, before I even configure backups.

It's particularly useful for:

  • Queue workers and scheduled commands where there's no visible user to complain when something breaks
  • Webhook receivers from third-party systems (payment processors, EHRs, etc.) where the format can change without warning
  • Multi-tenant SaaS apps where you need to know which tenant triggered an error
  • Any app where the client is not technical and won't notice silent failures

When I'd look elsewhere: If you're already paying for Datadog or New Relic for APM, their error tracking is good enough that you probably don't need Sentry too. Don't pay twice for the same thing. Also, if your only errors are JavaScript frontend errors and you're already on something like LogRocket for session replay, the overlap is significant — evaluate whether you need both.

For pure server-side PHP with no APM in place, nothing else at this price point comes close.

One More Thing on Cost

Sentry's pricing model changed a couple years ago from seat-based to event-based. That's better for small teams but it means a misbehaving deploy can spike your bill. Go into your project settings right now and set a Spike Protection threshold and a hard monthly error limit. These are in the Sentry dashboard under Settings > Projects > [your project] > Client Keys. Set a rate limit on the DSN itself as a backstop. I set mine to 500 events per minute — enough to capture a real incident, low enough that a loop doesn't run away.

You want Sentry to tell you when things are on fire. You don't want the fire to be your invoice.

Error monitoring is one of those things where the value is invisible until the day it saves you. I've had Sentry pay for a year of its own subscription in a single incident — caught a billing integration bug before it had processed enough bad transactions to be a real problem. Set it up, tune the noise filters, and then mostly forget about it. That's how it should work.

Need help shipping something like this? Get in touch.