---
title: "Maintenance windows & muting — Alerts for Laravel | Roundly"
description: "Mute a check, a tag or everything during deploys — for a set time or one owner — while runs and counters keep recording."
url: https://roundly-consulting.com/open-source/docs/alerts-for-laravel/muting
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)

# Maintenance windows & muting

Suppress alert notifications during deploys or maintenance while still recording runs. Health::silences() is the sub-accessor for maintenance windows:

```php
use RoundlyConsulting\Alerts\Facades\Health;

Health::silences()->mute('disk_usage_check', until: now()->addMinutes(30), reason: 'db migration');
Health::silences()->mute('disk_usage_check', until: now()->addHour());  // same key and scope: extends that one silence
Health::silences()->isMuted('disk_usage_check');   // true
Health::silences()->unmute('disk_usage_check');    // int: silences lifted

Health::silences()->mute('critical');              // mute a whole tag
Health::silences()->mute('*');                     // mute everything
Health::silences()->mute('disk_usage_check', for: $team);   // scope the mute to one owner
Health::silences()->unmute('disk_usage_check', for: $team); // lift only that owner's silence
```

- The key is a check key, a tag, or \* for everything. At run time a row is muted when a mute matches its check key, any of its effective tags, or \*.
- until is optional: without it the mute holds until you unmute it. mute() starts immediately and returns the AlertSilence record, with an optional reason.
- Muting the same key and scope again — global, or the same owner — updates that one silence instead of adding another: its reason and end are replaced, so it extends or shortens the window. A global and an owner-scoped silence of one key stay distinct, and two concurrent mutes still leave one row. A silence your code writes straight through the AlertSilence model has no identity and is never merged.
- for: scopes the mute to one owner. A mute without an owner applies to everyone.
- unmute() deletes the mutes for that key — the owner-scoped ones when you pass for:, otherwise only the global ones — and returns how many it lifted.
- isMuted() checks for an active mute on exactly the key you pass — a check key, a tag or \* — without expanding tags or \*. Without an owner it matches any mute on that key; with one, that owner’s mutes plus the global ones. It returns false whenever silence is off in the config.

## What happens while muted

Runs, history and counters are maintained, and alerts still open and close. HealthCheckFailed, HealthCheckRecovered, HealthCheckEscalated and every notification are suppressed. Touched alerts are flagged meta\['muted'\]; the flag follows the latest run, so it clears when failures continue after the window ends. The status report reads muted from the silences in force instead, by the same rule — so a muted check with no open alert shows as muted, and an alert opened under a silence that has since ended does not.

Set silence to false in the config to ignore every mute without deleting them.

## Listing mutes

active() returns the silences in force now — all of them, or the global ones plus those scoped to an owner. For your own queries, AlertSilence has the active() and matching() scopes behind it:

```php
use RoundlyConsulting\Alerts\AlertSilence;
use RoundlyConsulting\Alerts\Facades\Health;

Health::silences()->active();        // Collection<AlertSilence> in force now
Health::silences()->active($team);   // global silences + those scoped to $team

// The model scopes behind it, for your own queries:
AlertSilence::query()->active(now())->get();
AlertSilence::query()->matching(['critical'], $team)->exists();  // a 'critical' mute for this team, or a global one
```

## Is this row muted?

snapshot() loads every silence in force at one moment in a single query and answers for any number of rows with the run pipeline’s own rule: the row’s check key, one of its effective tags or \*, from a global silence or one scoped to the row’s own owner. covers() is the one-row shortcut. While silence is off in the config, the snapshot is empty:

```php
use RoundlyConsulting\Alerts\Facades\Health;

$silences = Health::silences()->snapshot();   // every silence in force now, one query; snapshot($at) for another moment

foreach ($team->healthChecks as $row) {
    $silences->covers($row);                  // muted by its key, one of its tags or *, globally or for its owner?
}

$silences->isEmpty();                         // nothing is muted at all
Health::silences()->covers($row);             // the one-row shortcut (one query)
```

Take one snapshot per request and ask it of every row — you can bind it as a scoped instance in the container. The status report uses one.

## Pruning ended silences

Ended windows would otherwise pile up. alerts:prune-silences — scheduled daily while silence is on — deletes silences that ended more than silence-retention days ago (7 by default); from code, call Health::silences()->prune(). Open-ended, running and future silences are never touched:

```bash
php artisan alerts:prune-silences --days=14
```

```php
use RoundlyConsulting\Alerts\Facades\Health;

Health::silences()->prune();    // ended more than alerts.silence-retention days ago; returns the number deleted
Health::silences()->prune(3);   // ended more than 3 days ago
Health::silences()->prune(0);   // throws InvalidRetention — the retention must be at least 1 day
```

\--days must be a whole number of at least 1: anything else prints an error, exits non-zero and deletes nothing.

## 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)
