Email Notifications
Dagu provides built-in email notifications for workflow events and errors.
Web UI notification routing
For team-wide Slack, email, Telegram, and webhook routing from the Web UI, use Notifications. For PagerDuty or SolarWinds incidents that open on final failure and resolve on recovery, use Incident Routing. Web UI notification routing is available on self-hosted Dagu without a license; Web UI incident routing still requires an active Dagu license or trial on self-hosted deployments.
The YAML fields on this page are useful when you want email behavior to travel with a DAG or base configuration. Web UI notification rules are better when operators should manage channels and event routing without editing DAG YAML.
SMTP Configuration
Base Configuration
Set up SMTP defaults in the base configuration inherited by DAGs:
# ~/.config/dagu/base.yaml
smtp:
host: smtp.gmail.com
port: "587"
username: alerts@example.com
password: app-specific-password
error_mail:
from: alerts@example.com
to: team@example.com # Single recipient (string format)
prefix: "[Dagu Alert]"
attach_logs: trueCredentials From Environment Or Secrets
smtp, error_mail, info_mail, and wait_mail are DAG/base-config fields. They are not read from server-level DAGU_* SMTP or mail environment variables.
If SMTP credentials come from the process environment, import them into DAG scope with env: or secrets: and reference the scoped variables from smtp:
env:
- SMTP_USER: ${SMTP_USER}
- SMTP_PASS: ${SMTP_PASS}
smtp:
host: smtp.gmail.com
port: "587"
username: "${SMTP_USER}"
password: "${SMTP_PASS}"For Web UI-managed notification rules, configure email delivery from Notifications instead of DAG YAML.
DAG-Level Configuration
Override global settings per DAG:
# my-dag.yaml
smtp:
host: smtp.company.com
port: "465"
username: ${SMTP_USER}
password: ${SMTP_PASS}
error_mail:
from: dagu@company.com
to:
- oncall@company.com
- manager@company.com
prefix: "[CRITICAL]"
attach_logs: true
mail_on:
success: true
failure: true
wait: true
wait_mail:
from: dagu@company.com
to:
- approvers@company.com
prefix: "[WAITING]"
attach_logs: falseEmail Triggers
Success/Failure/Wait Emails
mail_on:
success: true # Email on successful completion
failure: true # Email on failure
wait: true # Email when waiting for human approvalStep-Level Errors
steps:
- id: critical_step
run: process_critical_data.sh
mail_on_error: true # Email if this step failsWait Status Notifications
Send notifications when a DAG is waiting for human approval:
mail_on:
wait: true
wait_mail:
from: dagu@company.com
to:
- approvers@company.com
prefix: "[APPROVAL REQUIRED]"
attach_logs: falseThis is useful for workflows that require human approval before continuing execution. The email will include details about the DAG and which steps are waiting.
Mail Action
Send custom emails as workflow steps:
steps:
- id: send_report
action: mail.send
with:
to:
- reports@example.com
- archive@example.com
from: noreply@example.com
subject: "Daily Report - ${TODAY}"
message: |
Daily processing report for ${TODAY}
Summary:
- Records processed: ${RECORD_COUNT}
- Success rate: ${SUCCESS_RATE}%
- Processing time: ${DURATION}
See attached files for details.
attachments:
- /reports/daily-${TODAY}.pdf
- /reports/summary-${TODAY}.csv
- ${DAG_RUN_LOG_FILE}Email Templates
Processing Report
steps:
- id: generate_report
run: generate_report.py
output: REPORT_PATH
- id: email_report
action: mail.send
with:
to: stakeholders@example.com
subject: "Processing Report - ${DAG_NAME}"
message: |
Automated Report Generated
DAG: ${DAG_NAME}
Run ID: ${DAG_RUN_ID}
Status: Completed
Time: $(date)
Report available at: ${REPORT_PATH}
attachments:
- run: ${REPORT_PATH}
depends: generate_reportError Notification
handler_on:
failure:
action: mail.send
with:
to:
- oncall@example.com
- alerts@example.com
from: errors@example.com
subject: "⚠️ DAG Failed: ${DAG_NAME}"
message: |
DAG Execution Failed
Details:
- DAG: ${DAG_NAME}
- Run ID: ${DAG_RUN_ID}
- Time: $(date)
- Host: $(hostname)
Error Summary:
$(tail -20 ${DAG_RUN_LOG_FILE} | grep -i error)
Full log attached.
attachments:
- ${DAG_RUN_LOG_FILE}SMTP Providers
Gmail
smtp:
host: smtp.gmail.com
port: "587"
username: your-email@gmail.com
password: app-specific-password # Use app password, not regular passwordOffice 365
smtp:
host: smtp.office365.com
port: "587"
username: your-email@company.com
password: your-passwordSendGrid
smtp:
host: smtp.sendgrid.net
port: "587"
username: apikey
password: ${SENDGRID_API_KEY}AWS SES
smtp:
host: email-smtp.us-east-1.amazonaws.com
port: "587"
username: ${AWS_SES_SMTP_USERNAME}
password: ${AWS_SES_SMTP_PASSWORD}Advanced Configuration
Multiple Recipients
error_mail:
to:
- primary@example.com
- secondary@example.com
- team-alerts@example.comConditional Recipients
steps:
- id: check_environment
run: echo $ENVIRONMENT
output: ENV
- id: notify
action: mail.send
with:
to: |
`if [ "${ENV}" = "production" ]; then
echo "prod-alerts@example.com"
else
echo "dev-alerts@example.com"
fi`
subject: "Alert from ${ENV}"
message: "Environment-specific alert"
depends: check_environmentHTML Emails
steps:
- id: send_html_email
action: mail.send
with:
to: reports@example.com
subject: "HTML Report"
message: |
<html>
<body>
<h1>Daily Report</h1>
<table border="1">
<tr>
<td>Status</td>
<td style="color: green;">Success</td>
</tr>
<tr>
<td>Records</td>
<td>${RECORD_COUNT}</td>
</tr>
</table>
</body>
</html>
headers:
Content-Type: text/html