---
url: https://dozzle.dev/guide/alerts-and-webhooks.md
description: >-
  Set up Dozzle alerts on Docker container logs, CPU and memory metrics, and
  lifecycle events, and send them to a webhook, Slack, Discord, or ntfy.
---

# Alerts

Dozzle can watch container logs, resource metrics, and lifecycle events, and notify you when a condition you describe is met. Rules are written as expressions, evaluated on your own instance, and delivered to a webhook, Slack, Discord, or ntfy.

Rules always live here, on the self-hosted instance, because that is where your logs are. If you have linked the instance to [Dozzle Cloud](/guide/dozzle-cloud), these same rules feed it, and delivery (grouping, summaries, muting, mobile channels) is configured there instead of per-destination below.

## &#x20;Alert Types

Dozzle supports three kinds of alerts, all configured the same way from the **Notifications** page:

| Type                         | Triggers on                            | Example use case                |
| ---------------------------- | -------------------------------------- | ------------------------------- |
| [**Log**](#log-alerts)       | A log message matching a pattern       | 5xx errors, stack traces        |
| [**Metric**](#metric-alerts) | CPU / memory crossing a threshold      | Container exceeding 90% CPU     |
| [**Event**](#event-alerts)   | Container lifecycle events from Docker | OOM kills, unhealthy containers |

Each alert pairs a **container expression** (which containers to watch) with a **trigger expression** (the condition to fire on).

> \[!IMPORTANT]
> Alert and destination configurations are stored in the `/data` directory. You must mount this directory as a volume to persist your notification settings across container restarts.

::: code-group

```sh
docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/data:/data -p 8080:8080 amir20/dozzle:latest
```

```yaml [docker-compose.yml]
services:
  dozzle:
    image: amir20/dozzle:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /path/to/data:/data
    ports:
      - 8080:8080
```

:::

## &#x20;Setting Up a Destination

Before creating alerts, you need to configure at least one notification destination. Navigate to the **Notifications** page in Dozzle and click **Add Destination**.

### Webhook

Webhooks send an HTTP POST request to a URL of your choice. Dozzle includes built-in payload templates for popular services:

* **Slack** — formatted with blocks and markdown
* **Discord** — formatted for Discord webhook API
* **ntfy** — formatted for [ntfy.sh](https://ntfy.sh) push notifications
* **Custom** — generic JSON payload you can customize

You can also write your own payload template using Go's `text/template` syntax. The following variables are available:

| Variable                  | Description                            | Alerts |
| ------------------------- | -------------------------------------- | ------ |
| `{{.Detail}}`             | Summary (log message or metric values) | all    |
| `{{.Container.Name}}`     | Container name                         | all    |
| `{{.Container.Image}}`    | Container image                        | all    |
| `{{.Container.HostName}}` | Docker host name                       | all    |
| `{{.Container.State}}`    | Container state                        | all    |
| `{{.Log.Message}}`        | Log message content                    | log    |
| `{{.Log.Level}}`          | Log level                              | log    |
| `{{.Log.Timestamp}}`      | Log timestamp                          | log    |
| `{{.Log.Stream}}`         | Stream type (stdout/stderr)            | log    |
| `{{.Stat.CPUPercent}}`    | CPU usage percentage                   | metric |
| `{{.Stat.MemoryPercent}}` | Memory usage percentage                | metric |
| `{{.Stat.MemoryUsage}}`   | Memory usage in bytes                  | metric |
| `{{.Event.Name}}`         | Event name (event alerts)              | event  |
| `{{.Event.Attributes}}`   | Event attributes map (event alerts)    | event  |
| `{{.Subscription.Name}}`  | Alert rule name                        | all    |
| `{{.Timestamp}}`          | Time the notification was created      | all    |

`.Log`, `.Stat` and `.Event` are only set on their own kind of alert. A destination receives every kind, so a template that reads `{{.Log.Level}}` fails to send for metric and event alerts, including update notices. Guard those fields with `{{ with .Log }}{{ .Level }}{{ end }}`, or use `{{.Detail}}`, which every alert sets. The **Test** button warns when the template fails for a kind of alert it did not send.

`{{.Timestamp}}` prints in Go's default time format, which services like Discord reject. Format it as ISO 8601 instead:

```json
{
  "embeds": [{ "timestamp": "{{ .Timestamp.UTC.Format `2006-01-02T15:04:05Z07:00` }}" }]
}
```

> \[!TIP]
> Use the **Test** button to verify your webhook is working before saving.

#### Keeping the URL out of the file

Webhook URLs for Slack, Discord and similar services contain the token. To keep `notifications.yml` in version control, put the URL in an environment variable and reference it with `${VAR}` in the URL or any header value. The same works when typing into the form.

```yaml
dispatchers:
  - id: 1
    name: Slack
    type: webhook
    url: ${SLACK_WEBHOOK_URL}
    headers:
      Authorization: Bearer ${NTFY_TOKEN}
```

The placeholder is what Dozzle saves and shows. The value is read when a notification is sent, from the environment of whichever Dozzle sends it. With [agents](/guide/agent), alerts on an agent's containers are sent by that agent, so set the variable on each agent too. If it is missing there, the send fails with `environment variable ... is not set`.

Only the `${VAR}` form is expanded; a bare `$VAR` is left as is. Variables starting with `DOZZLE_`, `AWS_`, `GOOGLE_`, `AZURE_`, `KUBERNETES_` or `DOCKER_` are refused, since anyone who can edit a webhook decides where it posts. Any other variable in Dozzle's environment can be read this way, so don't keep unrelated secrets there.

### Dozzle Cloud&#x20;

Linked instances get **Dozzle Cloud** as a destination automatically. Unlike a raw webhook it groups repeated failures into a single notification, summarizes what happened, and fans out to email, Telegram, Discord, Slack, ntfy, and browser push without configuring each one here. See [Dozzle Cloud](/guide/dozzle-cloud).

## &#x20;Creating an Alert

Navigate to the **Notifications** page and click **Add Alert**. Every alert has a **container expression** plus one of a **log**, **metric**, or **event** trigger expression.

### Container Expression

The container expression selects which containers to monitor. Available properties:

| Property   | Type   | Example                         |
| ---------- | ------ | ------------------------------- |
| `name`     | string | `name contains "api"`           |
| `image`    | string | `image == "nginx:latest"`       |
| `state`    | string | `state == "running"`            |
| `health`   | string | `health == "unhealthy"`         |
| `hostName` | string | `hostName == "prod-host"`       |
| `labels`   | map    | `labels["env"] == "production"` |

You can combine conditions with `&&` (AND), `||` (OR), and `!` (NOT):

```
name contains "api" && labels["env"] == "production"
```

## &#x20;Log Alerts

### Log Expression

The log expression filters which log messages trigger the alert. Available properties:

| Property  | Type       | Example                    |
| --------- | ---------- | -------------------------- |
| `message` | string/map | `message contains "error"` |
| `level`   | string     | `level == "error"`         |
| `stream`  | string     | `stream == "stderr"`       |
| `type`    | string     | `type == "complex"`        |

For JSON logs, you can access nested fields using dot notation:

```
message.status >= 500 && message.path contains "/api"
```

Supported string operators include `contains`, `startsWith`, `endsWith`, and `matches` (regex).

### Log Examples

**Alert on all errors from production containers:**

```
Container: labels["env"] == "production"
Log:       level == "error"
```

**Alert on HTTP 5xx errors from API containers:**

```
Container: name contains "api"
Log:       message.status >= 500
```

**Alert on any stderr output from a specific image:**

```
Container: image startsWith "myapp/"
Log:       stream == "stderr"
```

**Alert on slow API responses from production:**

```
Container: name contains "api" && labels["env"] == "production"
Log:       message.duration > 5000 && message.path contains "/api"
```

**Alert on authentication failures using regex:**

```
Container: name contains "auth" || name contains "gateway"
Log:       message matches "(?i)(unauthorized|forbidden|invalid token)"
```

> \[!NOTE]
> The alert editor includes autocomplete and real-time validation. You can preview matched containers and logs before saving.

## &#x20;Metric Alerts

Metric alerts fire when a container's CPU or memory usage crosses a threshold. The trigger expression is evaluated against a smoothed average of stats sampled over a rolling window, which avoids false alarms from short spikes.

### Metric Expression

Available properties:

| Property      | Type   | Description                                   |
| ------------- | ------ | --------------------------------------------- |
| `cpu`         | number | CPU usage percentage (0–100), matching the UI |
| `memory`      | number | Memory usage percentage (0–100)               |
| `memoryUsage` | number | Memory usage in bytes                         |

### Cooldown & Sample Window

* **Sample window** — how many seconds of stats are averaged before the expression is evaluated. Longer windows smooth out spikes; shorter windows react faster.
* **Cooldown** — minimum seconds between consecutive triggers for the same container. Prevents alert floods when a container stays above threshold.

### Metric Examples

**High CPU on production containers:**

```
Container: labels["env"] == "production"
Metric:    cpu > 90
```

**Memory pressure on a specific service:**

```
Container: name contains "api"
Metric:    memory > 85
```

**Absolute memory usage (1 GiB):**

```
Container: name == "postgres"
Metric:    memoryUsage > 1073741824
```

## &#x20;Event Alerts

Event alerts fire on Docker container lifecycle events — useful for catching crashes, OOM kills, and health status changes without parsing logs.

### Event Expression

Available properties:

| Property     | Type   | Description                                         |
| ------------ | ------ | --------------------------------------------------- |
| `name`       | string | Event name (see below)                              |
| `actorId`    | string | Docker actor ID (usually the container ID)          |
| `attributes` | map    | Event attributes from Docker (varies by event type) |
| `timestamp`  | time   | When the event occurred                             |

Common Docker event names include `start`, `stop`, `die`, `kill`, `oom`, `restart`, `destroy`, and `health_status`.

For `health_status` events, Dozzle exposes the current state as `attributes["healthStatus"]` (`healthy` or `unhealthy`).

### Update Available

Dozzle also raises an `update_available` event when a newer image is published for a container. Once a day, at a random time, it checks every container not labeled `dev.dozzle.update: off`, and alerts once per new image. A restart of Dozzle can repeat an alert for an update it already sent. The check only runs while at least one event alert is enabled, and not at all when `--image-check-mode` is `manual` or `off`.

| Attribute        | Description                        |
| ---------------- | ---------------------------------- |
| `image`          | Image reference the container runs |
| `currentVersion` | Version of the running image       |
| `latestVersion`  | Version of the newer image         |
| `currentDigest`  | Digest of the running image        |
| `latestDigest`   | Digest of the newer image          |

Versions come from the image's `org.opencontainers.image.version` label. Many images don't set it, so either version can be empty. Dozzle also skips reading the new version when your Docker Hub pull limit is running low.

### Event Examples

**Alert when any production container dies:**

```
Container: labels["env"] == "production"
Event:     name == "die"
```

**Alert on OOM kills:**

```
Container: true
Event:     name == "oom"
```

**Alert when a container becomes unhealthy:**

```
Container: true
Event:     name == "health_status" && attributes["healthStatus"] == "unhealthy"
```

**Alert when a newer image is available:**

```
Container: true
Event:     name == "update_available"
```

In a webhook template, the versions are under `.Event.Attributes`:

```
New version for {{ .Container.Name }}: {{ .Event.Attributes.currentVersion }} -> {{ .Event.Attributes.latestVersion }}
```

**Alert on unexpected exits (ignoring clean and graceful shutdowns):**

Exit codes 0 (success), 130 (SIGINT), 143 (SIGTERM), and 137 (SIGKILL) fire on `docker stop`, Ctrl+C, and update cycles, so they are excluded to avoid noise. Genuine error exits (1, 2, 125, ...) still alert.

```
Container: name contains "worker"
Event:     name == "die" && !(attributes["exitCode"] in ["0", "130", "143", "137"])
```

## &#x20;Managing Alerts

From the Notifications page, you can:

* **Enable/disable** alerts without deleting them
* **Edit** alert expressions and destinations
* **View statistics** including trigger count, matched containers, and last triggered time
* **Delete** alerts that are no longer needed
