Raspberry Pi: send email alerts from shell scripts with msmtp
Configure msmtp on a Raspberry Pi so any shell script or cron job can send email alerts through your own SMTP relay. The pattern that survives.
Every Pi project eventually needs to tell you something. The disk is full. The backup did not run. The garage is still open at midnight. My answer to all of these is the same: an email from the command line. Not a push service, not a Telegram bot, not a SaaS dashboard. Email. It works on every device I own, it archives itself, and the script that sends it is three lines long.
The tool for this on a Pi is msmtp. It is not a mail server. It is a
tiny client that hands your message to a real SMTP server and walks
away. That distinction is the whole point: a Pi does not need to
receive mail, retry queues, or store anything. It needs to shout once
and get back to work.
What you need
Needed
- A Raspberry Pi running Raspberry Pi OS (any model; Lite is fine)
- An SMTP account that sends from your own infrastructure. A mail
- 20 minutes
Nice to have
- An anti-static wristband (cheap insurance around the Pi’s GPIO header)
- A small screwdriver kit for the case and camera ribbon clips
Install
One package. That is the entire install:
sudo apt update
sudo apt install msmtp msmtp-mta
msmtp-mta adds the sendmail shim, so anything on the Pi that expects
/usr/sbin/sendmail (e.g. cron’s own mail, logwatch, any script using
mail -s) just works without being reconfigured.
Configure
Create the config as root so system services can read it:
sudo nano /etc/msmtprc
Paste this in and edit the three lines that name your relay:
# /etc/msmtprc
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account default
host mail.yourdomain.com
port 587
from pi@yourdomain.com
user pi@yourdomain.com
password your-mail-password
Then lock the permissions down, because msmtp refuses to run if the config is world-readable (it treats your password as leaked and rightly so):
sudo chmod 600 /etc/msmtprc
Now the test. This is the part that saves you an hour later, so do not skip it:
echo "Test from the Pi" | msmtp you@yourdomain.com
No output means it sent. Check the inbox. If it is not there, the log
at /var/log/msmtp.log names the exact failure (auth failed, TLS
refused, DNS miss).
If your relay only speaks plain SMTP on port 25 inside your own network, that is fine too: set
tls offandport 25. Just make sure the Pi and the relay stay on the trusted side of your router.
The code
Any script can now send mail through one command. The alert about a full disk looks like this:
#!/bin/bash
# disk-alert.sh: email when root filesystem passes 90%
THRESHOLD=90
USAGE=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if [ "$USAGE" -ge "$THRESHOLD" ]; then
{
echo "Subject: Pi disk alert: ${USAGE}% full"
echo ""
echo "The root filesystem on $(hostname) is at ${USAGE}%."
echo "Time to find out what grew: du -xh / --max-depth=2 | sort -h"
} | msmtp you@yourdomain.com
fi
Notice the subject line is just a header in the message body. That is the sendmail convention: headers, a blank line, then the text. msmtp parses it out and the rest becomes the body.
Schedule it so the check happens whether you remember or not:
crontab -e
Add:
0 * * * * /home/pi/bin/disk-alert.sh
Every hour, on the hour, the script checks and stays quiet unless the disk crosses the line. An alert system that sends nothing is a system that works.
One step up: attach what went wrong instead of describing it. The backup-failure alert that includes the last 20 lines of the log:
tail -20 /var/log/backup.log | mail -s "backup failed on $(hostname)" you@yourdomain.com
(mail from the mailutils package uses the same sendmail shim, so
it goes out through msmtp without extra setup.)
What you learned
- msmtp is a send-only mail client: it authenticates to an SMTP relay you control, sends, and exits. Nothing listens, nothing stores.
- The sendmail shim (
msmtp-mta) makes every mail-sending tool on the Pi work through the same config. - The pattern in 1 sentence: any script, one pipe into
msmtp, and the alert is in your inbox with a timestamp you can search later.
When something breaks
- Nothing arrives, no error. Check
/var/log/msmtp.log. Nine times out of ten it is auth: the password has special characters that need quoting, or the relay expects the full email address as the username, not just the part before the @. - “Cannot connect to SMTP server”. The relay hostname does not
resolve from the Pi (e.g. you typoed it, or the Pi’s DNS is still on
the router and the relay is internal). Test with
ping mail.yourdomain.com, then fix the host line. - “sendmail: cannot log” or permission errors after an OS update.
Package updates occasionally reset config permissions. Re-run
sudo chmod 600 /etc/msmtprcand the send test again. - Cron runs the script but no mail. Cron’s PATH is minimal and it
does not inherit your shell environment. Use absolute paths inside
the script (
/usr/bin/msmtp, notmsmtp) and test the script by running it as cron would:env -i /bin/bash /home/pi/bin/disk-alert.sh. - Gmail-style providers reject the message. If you are relaying through a large provider instead of your own server, they may require an app password and refuse everything else. That is the account-expiry problem from the top of this post coming back. Own the relay, skip the dance.
What to build next
Pair the alert script with the SQLite logging tutorial: the logger emails you only when a sensor value crosses a threshold, and the database keeps the full history for the review afterwards. The ntfy notification tutorial is the lower-latency sibling for phone pings (e.g. email for the record, ntfy for the right now). If you want the alerts delivered anywhere in the world, the WireGuard tutorial gets the Pi reachable without exposing it. —Brian