Zabbix and Netdata Alert Delivery Using JustEmails SMTP
Wire the Zabbix Email media type and Netdata's sendmail path to authenticated SMTP — with rate limits so a flapping host can't torch your sender reputation.
Wire the Zabbix Email media type and Netdata's sendmail path to authenticated SMTP — with rate limits so a flapping host can't torch your sender reputation.
The Zabbix frontend said 341 notifications sent. My inbox had four. Broken SMTP alert delivery reads as perfectly healthy from the dashboard, which is exactly what makes it expensive.
That gap took an afternoon to explain, and I spent most of it blaming Gmail's spam filter for something Gmail had never once been asked to do. Zabbix was handing messages to a local Postfix that had never been configured to relay anywhere, so they queued, deferred, and eventually bounced into a maillog nobody was reading. Nobody being me.
Netdata on the same box was quieter about it. It just did nothing. No error in the UI, no red banner — because Netdata doesn't speak SMTP at all. It shells out to whatever sendmail binary it finds, and if there isn't one, the notification script exits and gets on with its life.
Same fix for both, and I'd argue it's the only correct one: stop treating alert email as a local-delivery problem and route it through an authenticated relay with real DNS behind it. Plus the part most guides skip — keeping a flapping host from firing 300 messages and wrecking your sending reputation in an afternoon.
health_alarm_notify.conf routing through msmtp to the same relaySame mailbox, same DKIM signature, same domain. If you've already got Grafana and Prometheus alerts going through SMTP, this slots into the exact same sending identity — no second relay, no second set of DNS records.
nc -zv smtp.justemails.app 587 answers that in two secondsIn your JustEmails dashboard, create a single mailbox for monitoring. alerts@yourdomain.com is fine.
One mailbox for both tools. The instinct is to give Zabbix and Netdata their own accounts — resist it. Two mailboxes means two passwords to rotate and two places to forget when you rotate them. I've rotated one and not the other, then read a TLS log for twenty minutes looking for a TLS problem that was never there. Split by From display name instead: "Zabbix" and "Netdata" in the header, same underlying account.
| Setting | Value |
|---|---|
| Host | smtp.justemails.app |
| Port | 587 |
| Security | STARTTLS |
| Username | alerts@yourdomain.com (full address) |
| Password | The mailbox password |
Mailboxes are unlimited on the $49/year plan, so if you later decide you do want per-datacenter senders, that costs nothing extra.
Go to Alerts → Media types, open the built-in Email entry, and fill it in:
| Field | Value |
|---|---|
| Type | |
| SMTP server | smtp.justemails.app |
| SMTP server port | 587 |
| SMTP helo | yourdomain.com |
| SMTP email | Zabbix [alerts@yourdomain.com](mailto:alerts@yourdomain.com) |
| Connection security | STARTTLS |
| SSL verify peer | ✅ checked |
| SSL verify host | ✅ checked |
| Authentication | Username and password |
| Username | alerts@yourdomain.com |
| Password | Your mailbox password |
| Message format | HTML |
Two fields people get wrong.
SMTP helo defaults to localhost on a lot of installs. Receivers score that badly — it's a classic signature of a misconfigured box. Put your actual domain there.
SMTP email is the From header, and it has to be a mailbox on a verified domain. The relay accepts your login and then rejects the message — auth succeeds, send fails, and the Zabbix log line is terse enough that you'll read past it twice.
Leave SSL verify peer and SSL verify host checked. I've seen people uncheck both to make a TLS error go away. That's not fixing the error, that's switching off the part that would have told you about it.
Click the Message templates tab. Zabbix ships defaults for Problem, Problem recovery, and Problem update, and the default subject is just Problem: {EVENT.NAME}.
That's not enough to filter on. Change the Problem subject to lead with severity and host:
[{EVENT.SEVERITY}] {HOST.NAME}: {EVENT.NAME}
And the body, so the on-call person has what they need without opening a laptop:
Host: {HOST.NAME} ({HOST.IP})
Severity: {EVENT.SEVERITY}
Started: {EVENT.TIME} on {EVENT.DATE}
Operational data: {EVENT.OPDATA}
{TRIGGER.DESCRIPTION}
Event ID: {EVENT.ID}
{TRIGGER.URL}
Mirror it on the recovery template with [RESOLVED] as the prefix. Sounds trivial. It isn't — a subject-line prefix is the difference between a Gmail filter that works and thirty minutes of squinting at a thread at 4 AM.
{EVENT.OPDATA} is the one I'd argue hardest for. It carries the triggering value — the disk percentage, the load average — into the preview text, so you can triage half your alerts without opening anything.
The media type exists but nothing routes to it yet. Two more steps.
User media: Users → Users → pick the user → Media tab → Add. Type = Email, Send to = the destination address, When active = 1-7,00:00-24:00, then check which severities should page. I check Warning and above for email; Information and Not classified go to a dashboard, not an inbox. Paging on Information severity is how a team learns to ignore the whole channel.
Trigger action: Alerts → Actions → Trigger actions. Create one with an operation that sends to the user via the Email media type. While you're in there, check Pause operations for suppressed problems — that's what makes maintenance windows actually silent instead of merely documented.
Test with a trigger you can break on purpose — an agent.ping check on a VM you can stop. The Zabbix media type's own test button only proves SMTP works. It doesn't prove your action, user media, and severity filters line up. Three separate places for a typo to hide.
Netdata's notification script calls sendmail. So install something that provides it. msmtp is the lightest option and it's what I run:
sudo apt install msmtp msmtp-mta # Debian/Ubuntu
sudo dnf install msmtp # RHEL/Fedora
Then /etc/msmtprc:
defaults
auth on
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account justemails
host smtp.justemails.app
port 587
from alerts@yourdomain.com
user alerts@yourdomain.com
password your-mailbox-password
account default : justemails
Now the part that eats an hour if you don't know it. Netdata runs as the netdata user, not root, and /etc/msmtprc lands mode 600 root-owned — so alarm-notify.sh can't read it, and says nothing about not being able to read it.
Don't chmod 644 your way out of that. A plaintext SMTP password readable by every account on the box isn't a fix, it's a different incident. (Stash the config at ~netdata/.msmtprc instead and you run into msmtp's other permission check — the one it applies to user config files, which refuses anything group- or world-readable with "contains secrets and therefore must have no more than user read/write permissions." It doesn't check the system-wide file that way, which is exactly why /etc/msmtprc is the better home for this.) Group ownership does it:
sudo chown root:netdata /etc/msmtprc
sudo chmod 640 /etc/msmtprc
Confirm it before moving on: sudo -u netdata msmtp --version should print, not complain.
Now the Netdata config. Don't hand-edit files in /etc/netdata — use the wrapper so upgrades don't clobber you:
sudo /etc/netdata/edit-config health_alarm_notify.conf
Set these four:
SEND_EMAIL="YES"
EMAIL_SENDER="Netdata [alerts@yourdomain.com](mailto:alerts@yourdomain.com)"
DEFAULT_RECIPIENT_EMAIL="oncall@yourdomain.com"
sendmail="/usr/bin/msmtp"
sendmail="" means autodetect, which usually works and occasionally finds the wrong binary on a host running both msmtp and a leftover Postfix. Be explicit.
Role-based routing while you're in here — Netdata tags every alarm with a role:
role_recipients_email[sysadmin]="oncall@yourdomain.com"
role_recipients_email[webmaster]="web-team@yourdomain.com"
role_recipients_email[dba]="db-team@yourdomain.com"
Restart and test:
sudo systemctl restart netdata
sudo -u netdata /usr/libexec/netdata/plugins.d/alarm-notify.sh test
That script prints every step and names exactly where it failed — more than the Netdata UI will ever do for you.
Here's the part that matters more than any of the config above.
A host that flaps every 90 seconds for six hours generates roughly 240 alert emails. Send those to a handful of Gmail recipients and you get rate-limited, then temp-failed, then — if someone hits "report spam" at 3 AM, which people absolutely do — you've got a complaint against a domain that also sends your customer email. Monitoring noise becomes a deliverability problem. Fast.
On the Zabbix side:
30m or 1h. People shorten it to 60 seconds during testing and then forgetlast(/host/key)>90 to fire and max(/host/key,#5)<80 to recover means five consecutive healthy samples before it clearsOn the Netdata side:
# in /etc/netdata/health.d/your-alarm.conf
delay: up 5m down 15m multiplier 1.5 max 1h
repeat: off
delay: up 5m holds the alarm five minutes before notifying, which swallows most transient spikes outright. The multiplier backs off further on repeat offenders. And repeat: off stops Netdata re-notifying on a still-active alarm.
Want a second path for the genuinely urgent stuff? That's what a calling platform like VeloCalls is for — a phone that actually rings beats shortening your email intervals and hoping someone's awake. Email is your audit trail. It shouldn't also be your pager.
The bounce and complaint handling guide is worth a read here — alert storms are one of the few ways a purely internal sending pattern damages a domain that's otherwise doing everything right.
Zabbix: "Cannot connect to SMTP server: Connection refused"
Outbound 587 is blocked. Host firewall first, then your cloud provider's security group — several block SMTP ports on new accounts until you ask.
Zabbix: "Cannot send email: SSL certificate verification failed"
Stale or missing CA bundle, common on minimal container images. apt-get install ca-certificates, restart the Zabbix server. Don't uncheck SSL verify peer. The TLS handshake troubleshooting guide covers the other causes.
Zabbix: auth succeeds, message never arrives, no error
The From domain in SMTP email isn't verified on your account. Check the exact spelling against your domain list — a .com where you meant .io produces precisely this. Ask me how long I stared at a perfectly healthy auth log before I thought to read the field underneath it.
Netdata: alarm-notify.sh test prints "command not found"
Plugin path differs by install method. Try /usr/libexec/netdata/plugins.d/alarm-notify.sh, then /opt/netdata/usr/libexec/netdata/plugins.d/alarm-notify.sh for static installs.
Netdata: nothing happens, no output, no email
Nine times out of ten it's the msmtp permissions problem from Step 5. Run sudo -u netdata msmtp --version. If that errors, you found it.
Both tools are now sending authenticated mail from one identity. A few things worth doing next:
[CRIT], [WARN], [RESOLVED] once and use them across every monitoring tool you runNone of this is clever. It's the difference between 341 notifications sent and 341 notifications read.
Go to Alerts → Media types → Email, then set the SMTP server to your relay host, port 587, and Connection security to STARTTLS. Set Authentication to "Username and password" and use the full email address as the username. The SMTP email field becomes the From header and must be a mailbox on a domain you control, otherwise the relay accepts the login and rejects the message.
Netdata doesn't speak SMTP — it shells out to the system sendmail binary. If no MTA is installed, or the netdata user can't read your msmtp config, alarm-notify.sh fails silently. Install msmtp, point sendmail in health_alarm_notify.conf at it, and confirm the netdata user can read the credentials file. Run alarm-notify.sh test to see the actual error.
Three controls stack. Set the escalation step duration on the trigger action to 30 minutes or an hour instead of the default. Enable "Pause operations for suppressed problems" so maintenance windows go quiet. And add hysteresis with a recovery expression so the trigger needs a sustained recovery, not one good poll, before it resolves and re-fires.
Yes, and it's usually the right call — one mailbox, one password to rotate, one DKIM-signed sending identity. Separate them by From display name and subject prefix instead of by account. The exception is a huge Netdata fleet where you want per-host From addresses; unlimited mailboxes on the $49/year plan makes that free to do.
Unlimited custom domain email hosting for $49/year flat — unlimited domains, unlimited mailboxes, 10 GB storage, full IMAP/SMTP. Built for agencies, freelancers, and anyone managing email across more than one domain.