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, these same rules feed it, and delivery (grouping, summaries, muting, mobile channels) is configured there instead of per-destination below.
Alert Types
Dozzle supports three kinds of alerts, all configured the same way from the Notifications page:
| Type | Triggers on | Example use case |
|---|---|---|
| Log | A log message matching a pattern | 5xx errors, stack traces |
| Metric | CPU / memory crossing a threshold | Container exceeding 90% CPU |
| Event | 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.
docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/data:/data -p 8080:8080 amir20/dozzle:latestservices:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /path/to/data:/data
ports:
- 8080:8080Setting 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 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:
{
"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.
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, 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
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.
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"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 >= 500Alert 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.
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 > 90Memory pressure on a specific service:
Container: name contains "api"
Metric: memory > 85Absolute memory usage (1 GiB):
Container: name == "postgres"
Metric: memoryUsage > 1073741824Event 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"])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