I want my Mac to email me when a cron job fails. By default it can’t: macOS installs Postfix but leaves it unconfigured for outbound mail. Here I point Postfix at Gmail’s SMTP (Simple Mail Transfer Protocol) relay so system mail leaves the machine.

I first set this up on macOS Catalina. The same steps still work on current macOS because Postfix remains the default mail transfer agent.

If you run Unix or Linux instead, follow my Unix/Linux version of this guide.

Why would you want to do this?

  • You have cron jobs running on your system and need email when they fail.
  • You want an email when certain things happen on your system.

Before you start

  • Administrator (sudo) access on the Mac.
  • A Gmail account with 2-Step Verification enabled.
  • A Google app password generated for this Mac.
  • About 10 minutes.

Back up the Postfix config

I always back up system files before editing them so I can roll back if mail breaks.

sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.bak

Store the Gmail credentials

Create the password map file:

sudo vi /etc/postfix/sasl_passwd

Add one line, substituting your Gmail address and app password:

[smtp.gmail.com]:587 your-email@gmail.com:your-app-password

Compile the password map so Postfix can read it:

sudo postmap /etc/postfix/sasl_passwd

Confirm the compiled .db file exists:

ls -l /etc/postfix/sasl_passwd.db

You should see the file listed. If it is missing, postmap failed and the test email later will silently drop.

Lock down permissions so only root can read the password:

sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

Point Postfix at the Gmail relay

Edit the main Postfix config:

sudo vi /etc/postfix/main.cf

Append this block to the bottom of the file. It tells Postfix to authenticate via SASL (Simple Authentication and Security Layer) and encrypt with TLS (Transport Layer Security). The upstream Postfix SASL README documents every directive:

# Gmail SMTP relay
relayhost = [smtp.gmail.com]:587

# Enable SASL authentication in the Postfix SMTP client.
smtpd_sasl_auth_enable = yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options =
smtp_sasl_mechanism_filter = AUTH LOGIN

# Enable Transport Layer Security (TLS), i.e. SSL.
smtp_use_tls = yes
smtp_tls_security_level = encrypt
tls_random_source = dev:/dev/urandom

Start Postfix and keep it running

macOS ships Postfix but leaves its launchd (launch daemon) job disabled, so on a fresh Mac the reload below fails like this:

postfix/postfix-script: fatal: the Postfix mail system is not running

Check whether the job is loaded:

sudo launchctl list | grep postfix

An empty result means the job is not running. Clear the disabled flag so launchd keeps starting it across reboots, then start it now:

sudo launchctl enable system/com.apple.postfix.master
sudo launchctl kickstart -k system/com.apple.postfix.master

Do not reach for launchctl bootstrap here. macOS already bootstraps everything in /System/Library/LaunchDaemons at boot, and System Integrity Protection (SIP) will not let you load that plist a second time, so the command fails with:

Bootstrap failed: 5: Input/output error

enable is what clears the persistent disabled flag, and kickstart -k is what restarts an already-bootstrapped job. Confirm Postfix is up:

sudo postfix status

You should see postfix/postfix-script: the Postfix mail system is running: PID 1234.

Two details are worth knowing here. launchctl enable writes a persistent override, so the job stays enabled after a restart. And the macOS Postfix job is queue-triggered: launchd starts master when mail lands in the queue and lets it exit once the queue drains. On an idle Mac postfix status can therefore report it stopped even though delivery still works, so do not chase that as a problem on its own.

Reload Postfix

Reload Postfix so it picks up the new config:

sudo postfix reload

You should see postfix/postfix-script: refreshing the Postfix mail system with no errors. If you get the fatal: message instead, Postfix stopped again: run sudo postfix start and re-run the reload.

Test delivery

Send yourself a test message:

date | mail -s "Test Email" your-email@gmail.com

The email should arrive within a minute. If it does, you are done. Otherwise, read on.

Troubleshooting

If the test email never arrives, tail Postfix’s logs while you resend the test:

log stream --predicate '(process == "smtpd") || (process == "smtp")' --info

Common error strings and their fixes:

  • 535-5.7.8 Username and Password not accepted: the app password is wrong, or the Gmail account lacks 2-Step Verification. Regenerate the app password, rewrite /etc/postfix/sasl_passwd, then re-run sudo postmap /etc/postfix/sasl_passwd.
  • SSL_connect error or TLS handshake failures: smtp_use_tls = yes and smtp_tls_security_level = encrypt are missing or misspelled in main.cf.
  • no mechanism available: smtp_sasl_mechanism_filter = AUTH LOGIN is missing, or smtp_sasl_security_options = was omitted. Double-check the block above.
  • Nothing appears in the log at all: the mail command never reaches Postfix. Confirm Postfix is running with sudo postfix status, and if it is not, work through Start Postfix and keep it running again.

Roll back

Restore the original config and clear the credentials:

sudo cp /etc/postfix/main.cf.bak /etc/postfix/main.cf
sudo rm /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo postfix reload

References