Blog

Victor is a full stack software engineer who loves travelling and building things. Most recently created Ewolo, a cross-platform workout logger.

    Setup postfix on Ubuntu and send emails for failed cron jobs

    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!

    HackerNews submission / discussion

    Back to the article list.

    SmallData newsletter

    Subscribe to get articles as they are published direct to your inbox!