---
title: Maintenance windows
description: Schedule planned work so alerts stay quiet, uptime stays fair, and users are told in advance.
sidebar:
  label: Maintenance windows
---

{/* Source of truth: packages/shared/src/maintenance-windows.ts —
    createMaintenanceWindowSchema, recurringScheduleSchema. Routes:
    monitors.maintenanceWindows.* (attached to MONITORS, not status pages).
    DB guarantees: maintenance_windows_no_overlap (GiST exclusion),
    maintenance_windows_valid_range CHECK. Plan gate: maxMaintenanceWindows. */}

A maintenance window tells Glowo that downtime is expected. Alerts are
suppressed, the component shows `UNDER_MAINTENANCE`, and — by default — the time
is excluded from uptime.

:::note
Maintenance windows are attached to **monitors**, not to status pages:

```http
POST /monitors/<monitor-id>/maintenance-windows
```

A monitor shown on several status pages shows as under maintenance on all of
them.
:::

## Fields

| Field | Value | Default |
| --- | --- | --- |
| `title` | 1–255 characters | required |
| `description` | up to 2000 characters | none |
| `startsAt` | ISO 8601 timestamp | required |
| `endsAt` | ISO 8601 timestamp | required |
| `timezone` | IANA timezone name | `UTC` |
| `recurring` | see below | none |
| `excludeFromUptime` | boolean | `true` |

`endsAt` must be after `startsAt` — enforced both in the domain and by a
database constraint.

## Recurring windows

| `frequency` | Extra field |
| --- | --- |
| `daily` | — |
| `weekly` | `dayOfWeek` (0–6, Sunday is 0) |
| `monthly` | `dayOfMonth` (1–31) |
| `cron` | `expression` |

Set `timezone` for anything recurring. A window defined in `UTC` drifts by an
hour relative to local working time when daylight saving changes, and will
eventually cover the wrong part of your night.

:::caution
`dayOfMonth: 31` does not occur in every month. Prefer a `cron` expression when
you need precise monthly behaviour.
:::

## Windows cannot overlap

Two maintenance windows on the same monitor may not overlap. This is enforced by
a database exclusion constraint, not just by the UI, so it holds even for
concurrent requests.

If a window is rejected, an existing one already covers part of that period —
extend it rather than adding a second.

## excludeFromUptime

Leave it `true` for genuine planned maintenance: you agreed the downtime in
advance, and counting it against your SLA is not meaningful.

Set it `false` when you want the window only to suppress alerts while still
counting the impact — useful when you are being held to an availability number
that does not carve out maintenance.

:::caution
Excluding maintenance from uptime is a reporting choice with real consequences.
If you publish an SLA, make sure your definition of uptime matches what your
contract says, and be able to show the excluded windows — the
[SLA report](/monitors/reports) reports paused and excluded time separately for
exactly this reason.
:::

## Maintenance versus pausing

| | Maintenance window | [Pausing](/monitors) |
| --- | --- | --- |
| Scheduled in advance | yes | no |
| Visible to users | yes | no |
| Checks keep running | yes | no |
| Right for | planned work | ad-hoc work, or a monitor you are fixing |

Because checks keep running during maintenance, you can see whether the work
actually finished on time.
