---
title: "Configuration — Alerts for Laravel | Roundly"
description: "Every config key with its default and env variable — models, key type, job, scheduling, endpoint, muting, history and escalation."
url: https://roundly-consulting.com/open-source/docs/alerts-for-laravel/configuration
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Alerts for Laravel](https://roundly-consulting.com/open-source/docs/alerts-for-laravel.md)

# Configuration

The published config/alerts.php, without its comment blocks:

```php
<?php

declare(strict_types=1);

use RoundlyConsulting\Alerts\Alert;
use RoundlyConsulting\Alerts\AlertSilence;
use RoundlyConsulting\Alerts\HealthCheck;
use RoundlyConsulting\Alerts\HealthCheckRun;
use RoundlyConsulting\Alerts\Jobs\HealthCheckJob;

return [
    'health-check' => HealthCheck::class,

    // Check classes (or instances) registered globally on boot.
    'checks' => [
        // RoundlyConsulting\Alerts\Checks\DatabaseCheck::class,
    ],

    'alert' => Alert::class,

    // Key type of your notifiable (owner) models: bigint | uuid | ulid
    'key_type' => env('ALERTS_KEY_TYPE', 'bigint'),

    'job' => HealthCheckJob::class,

    // Auto-register the perform command on the scheduler.
    'schedule' => [
        'enabled' => env('ALERTS_SCHEDULE', true),
        'frequency' => env('ALERTS_SCHEDULE_FREQUENCY', 'everyMinute'),
    ],

    // Opt-in JSON status endpoint registered via Health::routes().
    'route' => [
        'uri' => env('ALERTS_ROUTE_URI', 'health'),
        'name' => 'alerts.health',
        'details' => env('ALERTS_ROUTE_DETAILS', false),
    ],

    // Master switch for maintenance-window muting + the silence model.
    'silence' => env('ALERTS_SILENCE', true),
    'silence-model' => AlertSilence::class,
    'silence-retention' => env('ALERTS_SILENCE_RETENTION', 7),

    // Run history + latency tracking, retention, and the run model.
    'history' => [
        'enabled' => env('ALERTS_HISTORY', true),
        'retention_days' => env('ALERTS_HISTORY_RETENTION', 30),
        'model' => HealthCheckRun::class,
    ],

    // Store (and mail) a thrown exception's message and frames — off by default.
    'store_exception_details' => env('ALERTS_STORE_EXCEPTION_DETAILS', false),

    // Optional global default escalation policy (data-only).
    'escalation' => [
        // 1 => 'owner', 3 => 'team', 5 => 'oncall',
    ],
];
```

## Every key

| Key | Default | Env | Purpose |
| --- | --- | --- | --- |
| `health-check` | HealthCheck::class | — | Model that stores scheduled checks. |
| `checks` | \[\] | — | Check classes (or instances) registered on boot. |
| `alert` | Alert::class | — | Model that records alerts. |
| `key_type` | bigint | ALERTS\_KEY\_TYPE | Key type of the notifiable\_id columns on health\_checks, alerts and alert\_silences — bigint, uuid or ulid (case-insensitive; anything else throws InvalidConfigurationException). Read when the migrations run, so set it first. |
| `job` | HealthCheckJob::class | — | Queued job dispatched for each due check. A blank value is not set and uses the packaged job. |
| `schedule.enabled` | true | ALERTS\_SCHEDULE | Auto-register alerts:perform-health-checks on the scheduler, onOneServer() with a 5-minute overlap lock. Pruning is scheduled either way while history is enabled. |
| `schedule.frequency` | everyMinute | ALERTS\_SCHEDULE\_FREQUENCY | Scheduler method used for the command: everyMinute, everyTwoMinutes, everyThreeMinutes, everyFourMinutes, everyFiveMinutes, everyTenMinutes, everyFifteenMinutes, everyThirtyMinutes, hourly, everyOddHour, everyTwoHours, everyThreeHours, everyFourHours, everySixHours, daily, twiceDaily, weekly, monthly, quarterly or yearly. A blank value is not set (everyMinute); any other name throws when the scheduler is resolved. A coarser cadence still runs every check: each tick queues the checks that came due since the previous one. |
| `route.uri` | health | ALERTS\_ROUTE\_URI | URI of the opt-in JSON status endpoint. A blank value is not set and uses health — never the site root; a non-string value throws. |
| `route.name` | alerts.health | — | Route name of the status endpoint. A blank value is not set and uses the default; a non-string value throws. |
| `route.details` | false | ALERTS\_ROUTE\_DETAILS | Also render message, tags, last\_alert\_at and muted for each check on the endpoint — an explicit allow-list: the raw alert meta never renders. Off by default: the endpoint is unauthenticated unless you add middleware, and with store\_exception\_details on a stored message can carry raw exception text. |
| `silence` | true | ALERTS\_SILENCE | Master switch for maintenance-window muting; false ignores every silence. While on, alerts:prune-silences is scheduled daily. |
| `silence-model` | AlertSilence::class | — | Model that stores mute records. |
| `silence-retention` | 7 | ALERTS\_SILENCE\_RETENTION | Days an ended silence is kept before alerts:prune-silences deletes it. Must be a whole number of at least 1; blank is not set (7); anything else throws, naming the key. |
| `history.enabled` | true | ALERTS\_HISTORY | Record every run (status + latency) and schedule alerts:prune-runs daily — also when schedule.enabled is off. |
| `history.retention_days` | 30 | ALERTS\_HISTORY\_RETENTION | Days of run history kept before alerts:prune-runs deletes them. Must be a whole number of at least 1; blank is not set (30); anything else (five, 5.5, 0) throws instead of pruning, so a typo can never wipe the run history. |
| `history.model` | HealthCheckRun::class | — | Model that records runs. |
| `store_exception_details` | false | ALERTS\_STORE\_EXCEPTION\_DETAILS | Store — and mail — a thrown exception’s message and stack frames, and the error a built-in check caught, on runs and alerts. Off by default: they can carry hosts, SQL and credentials, so the stored result names only the exception’s class and the throwable goes to your exception handler instead. |
| `escalation` | \[\] | — | Global default escalation policy (threshold ⇒ group), applied to any check that declares none of its own. |

## Environment

```bash
ALERTS_KEY_TYPE=bigint
ALERTS_SCHEDULE=true
ALERTS_SCHEDULE_FREQUENCY=everyMinute
ALERTS_ROUTE_URI=health
ALERTS_ROUTE_DETAILS=false
ALERTS_SILENCE=true
ALERTS_SILENCE_RETENTION=7
ALERTS_HISTORY=true
ALERTS_HISTORY_RETENTION=30
ALERTS_STORE_EXCEPTION_DETAILS=false
```

The switches — schedule.enabled, route.details, silence, history.enabled and store\_exception\_details — accept booleans and the usual env strings: 1, true, on and yes turn them on; 0, false, off and no turn them off. A blank value (ALERTS\_SCHEDULE=) is not set, so the switch keeps its default (off for route.details and store\_exception\_details, on for the rest). Any other value throws InvalidConfigurationException naming the key and the value, so a typo never silently flips a switch.

Every non-boolean setting is read strictly too: a key that is not set — absent, null or blank (a host’s KEY=) — takes its default, and any other invalid value throws InvalidConfigurationException naming the key. Nothing falls back silently.

Keep the checks array to class names. Configured instances such as DatabaseCheck::make('mysql') are better registered from a service provider — objects in a config file stop php artisan config:cache from serializing it.

## Swapping models

Every model key may point at your own subclass of the packaged model. Each is resolved through a single seam, so a swapped model is honoured everywhere — relations, actions, commands and the report alike:

```php
namespace App\Models;

use RoundlyConsulting\Alerts\HealthCheck as BaseHealthCheck;

class HealthCheck extends BaseHealthCheck
{
    // your own scopes, relations, observers…
}
```

```php
// config/alerts.php
'health-check' => App\Models\HealthCheck::class,
'alert' => App\Models\Alert::class,
'silence-model' => App\Models\AlertSilence::class,
'history' => [
    // …
    'model' => App\Models\HealthCheckRun::class,
],
```

A key that is not set — absent, null or blank — resolves the packaged model. Anything else must be the packaged model or a subclass of it — the package relies on its methods and scopes — otherwise it throws InvalidConfigurationException naming the key. A foreign class is never silently replaced.

## Swapping the job

The job key sets the queued job dispatched for each due check. It is dispatched with the HealthCheck row as its only argument; the packaged HealthCheckJob is final, so write your own and run the row with Health::for($row->notifiable)->run($row) — for example to run monitoring on a dedicated queue. The pipeline behind it, RunHealthCheckAction, is internal; don’t call it directly:

```php
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use RoundlyConsulting\Alerts\Facades\Health;
use RoundlyConsulting\Alerts\HealthCheck;

class MonitoringJob implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    public function __construct(public HealthCheck $healthCheck)
    {
        $this->onQueue('monitoring');
    }

    public function handle(): void
    {
        // Runs this scheduled row as-is, through the same pipeline as HealthCheckJob.
        Health::for($this->healthCheck->notifiable)->run($this->healthCheck);
    }
}

// config/alerts.php
'job' => App\Jobs\MonitoringJob::class,
```

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/alerts-for-laravel.md)
