A failing disk, a cron job that exits 1, a systemd unit that keeps restarting: Linux tells you about all of it by email. By default that email lands in a local mailbox on the machine, which nobody reads.

This guide points that mail at Gmail. Set it up once with an app password, and cron and systemd alerts arrive in a mailbox you already check. Both configurations below were tested on Debian 12 with msmtp 1.8.23 and s-nail 14.9.24.

Before You Start

You need:

  • A Linux machine where you can run sudo.
  • A Google account with 2-Step Verification turned on. Google only issues app passwords to accounts that have it.
  • An app password, which is a 16-character credential that lets one program sign in without your account password or a second factor.

Create the app password at Google’s app passwords page, name it after the machine, and copy it somewhere safe. Google shows it once.

Treat that string like a password, because it is one. Anything holding it (a config file, a screenshot, a terminal recording, a blog post) is holding a working credential for your mailbox. When one leaks, revoke it on the same page; revoking blocks the program that used it and nothing else.

Choose a Tool

msmtp A small SMTP client. One config file, clear error messages, and a log file. It’s the simpler path if nothing on the machine already sends mail.
mailx or s-nail The traditional mail command, configured through mail.rc. Already present on many systems, and the tool most scripts call. Choose this if your scripts already pipe to mail.

Both talk to smtp.gmail.com. Use port 587 with STARTTLS, or port 465 for implicit TLS.

Path A: Send Mail With msmtp

Install it:

# Debian or Ubuntu
sudo apt-get install msmtp msmtp-mta ca-certificates

# Fedora or RHEL
sudo dnf install msmtp ca-certificates

Write /etc/msmtprc, replacing the user and password values:

defaults
auth           on
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        /var/log/msmtp.log

account        gmail
host           smtp.gmail.com
port           587
from           you@gmail.com
user           you@gmail.com
password       your-16-char-app-password

account default : gmail

Restrict the file, because it holds a credential:

sudo chown root:root /etc/msmtprc
sudo chmod 600 /etc/msmtprc

Check that msmtp reads the config without connecting:

printf 'Subject: config check\n\nbody\n' | msmtp --pretend you@example.com
loaded system configuration file /etc/msmtprc
falling back to default account
using account default from /etc/msmtprc
host = smtp.gmail.com
port = 587

Then send a real message:

printf 'Subject: msmtp test\n\nIt works.\n' | msmtp you@gmail.com

Silence means success. Installing msmtp-mta also points /usr/sbin/sendmail at msmtp, so anything that expects a local mail transfer agent starts working too.

Path B: Configure mail.rc for mailx or s-nail

The mail command reads its settings from /etc/mail.rc (system-wide) or ~/.mailrc (per user). On Debian and Ubuntu the package is s-nail or bsd-mailx; on Fedora and RHEL 9, s-nail replaced the old mailx package and still provides the mail command.

# Debian or Ubuntu
sudo apt-get install s-nail ca-certificates

# Fedora or RHEL
sudo dnf install s-nail ca-certificates

Add this to /etc/mail.rc:

set mta=smtps://smtp.gmail.com:465
set smtp-auth=login
set smtp-auth-user=you@gmail.com
set smtp-auth-password=your-16-char-app-password
set from="you@gmail.com (Server Alerts)"

Restrict the file for the same reason as above:

sudo chmod 600 /etc/mail.rc

Older versions of this guide, and many others, set ssl-verify=ignore to get past certificate errors. Leave it out. It disables certificate checking, which lets anything between the machine and Gmail read the session, credential included. Install ca-certificates instead, which is the actual fix.

Send a test message:

echo "It works." | mail -v -s "mailx test" you@gmail.com

With -v, a working send prints the SMTP conversation. The AUTH LOGIN exchange contains your username and app password as base64, which is encoding, not encryption, and decodes in one command. Redact those lines before pasting the output anywhere:

Connecting to 74.125.199.108:465 . . . connected.
220 smtp.gmail.com ESMTP - gsmtp
>>> EHLO server.example.com
250-smtp.gmail.com at your service
250-AUTH LOGIN PLAIN XOAUTH2
>>> AUTH LOGIN
334 VXNlcm5hbWU6
>>> [base64 of your Gmail address: redact this]
334 UGFzc3dvcmQ6
>>> [base64 of your app password: redact this]
235 2.7.0 Accepted
>>> MAIL FROM:<you@gmail.com>
250 2.1.0 OK
>>> RCPT TO:<you@gmail.com>
250 2.1.5 OK
>>> DATA
354  Go ahead
>>> .
250 2.0.0 OK
>>> QUIT
221 2.0.0 closing connection

Route cron and systemd Alerts to Gmail

Cron mails any output a job produces to the address in MAILTO:

MAILTO="you@gmail.com"
0 3 * * * /usr/local/bin/backup.sh

A job that prints nothing sends nothing, so a silent success stays silent.

For systemd, create a unit that emails a failure report:

# /etc/systemd/system/status-email@.service
[Unit]
Description=Email the status of %i

[Service]
Type=oneshot
ExecStart=/bin/sh -c '{ echo "Subject: %i failed on $(hostname)"; echo; systemctl status --full --lines=50 %i; } | /usr/bin/msmtp you@gmail.com'
User=root

The echo lines matter: a message with no Subject header arrives in Gmail with an empty subject line. The blank line after it separates the headers from the body.

Attach the unit to any service you care about:

# /etc/systemd/system/backup.service.d/override.conf
[Unit]
OnFailure=status-email@%n.service
sudo systemctl daemon-reload
sudo systemctl start status-email@backup.service   # test it without waiting for a failure

Troubleshooting

535 Username and Password Not Accepted

SMTP server: 535-5.7.8 Username and Password not accepted. For more information, go to
SMTP server: 535 5.7.8  https://support.google.com/mail/?p=BadCredentials

Gmail rejected the credential. Check that:

  • You used the app password, not your Google account password.
  • 2-Step Verification is still on. Turning it off invalidates app passwords.
  • The password has no spaces. Google displays it in four groups of four; type it as 16 characters.
  • The app password hasn’t been revoked.

msmtp reports the same failure like this:

msmtp: authentication failed (method PLAIN)
msmtp: server message: 535-5.7.8 Username and Password not accepted.
msmtp: could not send mail (account default from /etc/msmtprc)

Certificate Errors

Install the trust store, and leave verification on:

sudo apt-get install ca-certificates   # or: sudo dnf install ca-certificates

Test the connection on its own. This prints the certificate and the negotiated TLS version without sending anything or authenticating:

msmtp --serverinfo --host=smtp.gmail.com --port=587 --tls --tls-starttls
SMTP server at smtp.gmail.com, port 587:
TLS session parameters:
    (TLS1.3)-(ECDHE-X25519)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)
TLS certificate information:
    Subject:
        CN=smtp.gmail.com
    Issuer:
        C=US,O=Google Trust Services,CN=WR2

Nothing Arrives, and Nothing Fails

Check the log and the local mailbox:

sudo tail /var/log/msmtp.log
sudo journalctl -u cron --since today
ls -l /var/mail/"$USER"

Mail that stayed local means the sending tool never ran. Mail that left the machine but never arrived usually sits in Gmail’s spam folder on the first send.

Connection Times Out

Many home and cloud providers block outbound port 25, and some block 465. Port 587 usually survives. Test reachability:

timeout 5 bash -c 'cat < /dev/null > /dev/tcp/smtp.gmail.com/587' && echo open || echo blocked

Keep the Credential Safe

  • Give each machine its own app password, so revoking one doesn’t silence the rest.
  • Keep config files at mode 600, owned by root. Anything readable by other users hands them your mailbox.
  • Never paste verbose SMTP output into a ticket, a gist, or a blog post without redacting the AUTH LOGIN lines.
  • With msmtp, keep the password out of the file entirely by fetching it at send time:
passwordeval "gpg --quiet --for-your-eyes-only --decrypt ~/.msmtp-gmail.gpg"
  • Revoke an app password the moment a machine is retired or a credential is exposed.

References