Recently, I learned that on linux cron comes with a very simple email notification setup, namely the
MAILTO
environment variable. In this article we will setup postfix to send email via an external smtp
server and ask cron to send emails on failed jobs. This requires that we have a domain e.g. domain.net that we can use
to send emails as.
Smtp host
To begin with, we will need an smtp host that lets us send emails. While one can use gmail, it requires a fair amount of hoop jumping to enable access for less secure apps and needs to constantly be used otherwise it is disabled automatically. I can recommend migadu for their barebones plan that lets one configure multiple domains and senders. For the purposes of this article, we only need 1 domain that we can send emails from. Once a smtp provider is configured and a sender username/password obtained, we can proceed.
Note that if using migadu, it is important to setup a mailbox for the machine name that will
be sending the emails as well as configuring subdomain wildcards sending. This is because on my machine atleast cron
sends the emails as user@host.domain.net
and with subdomain wildcards any email from/to
subdomain.domain.net
gets put into the subdomain@domain.net
mailbox.
Setup postfix
Install:sudo apt install postfix mailutils
configure the smtp relay in /etc/postfix/main.cf
:
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = subdomain.domain.net
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = $myhostname, domain.net, localhost.localdomain, localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
#default_transport = error
relay_transport = error
inet_protocols = ipv4
relayhost = [smtp.migadu.com]:465
smtp_sasl_auth_enable=yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_use_tls = yes
# following 2 are required by migadu
smtp_tls_wrappermode = yes
smtp_tls_security_level = encrypt
Note that most other smtp relays will require the relayhost
to be [mail.example.com]:587
with the tls wrapper mode and security level left at their defaults.
Now configure the smtp password via /ec/postfix/sasl_passwd
(only need one that matches the entry in
relayhost
):
# Gmail:
[smtp.gmail.com]:587 user@example.org:password
# Mailgun:
[smtp.mailgun.org]:587 user@example.com:password
# Amazon SES:
[email-smtp.us-east-1.amazonaws.com]:587 user:pass
Hash the password: sudo postmap /etc/postfix/sasl_passwd
and restart the service
sudo service postfix restart
.
Test sending emails via echo "Hello world!" | mail -s "email test $(date +%s)" vic@smalldata.tech
.
Note that
mail
takes a parameter -r
that lets one change the sender address. To check for errors, see
/var/log/mail.log
. Fortunately, email is a very old system and smtp providers actually provide useful
error messages when things don't work! Remember that the postfix service needs to be restarted on any change to the
main
configuration file.
Most email providers generally overwrite the sender address based on the account that is used to log in, however
migadu specifically requires that the correct sender address be used. Normally postfix simply uses
user@hostname
as the sender address so it might be required to rewrite it to something more legitimate:
echo "user@hostname subdomain@domain.net" | sudo tee -a /etc/postfix/generic
echo "smtp_generic_maps = hash:/etc/postfix/generic" | sudo tee -a /etc/postfix/main.cf
sudo postmap /etc/postfix/generic
sudo service postfix restart
Cron alerts
The last piece is to set the MAILTO
variable at the start of the cron configuration and thereafter
setup the jobs as the following which will send regular output to the specified file and email the output of any
failure:
MAILTO=vic@smalldata.tech
# run every evening at 22:06
6 22 * * * /home/user/bin/backup.sh > /home/user/cron-logs/backup.log
If the automatic email is not sent out, one can also force output to be sent as email via:
2>&1 | mail -s "subject" box@mailbox.com
.
Besides cron job notifications, we can now send emails programmatically from our system which is pretty cool :). Happy emailing!