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
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-certificatesWrite /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 : gmailRestrict the file, because it holds a credential:
sudo chown root:root /etc/msmtprc
sudo chmod 600 /etc/msmtprcCheck that msmtp reads the config without connecting:
printf 'Subject: config check\n\nbody\n' | msmtp --pretend you@example.comloaded system configuration file /etc/msmtprc
falling back to default account
using account default from /etc/msmtprc
host = smtp.gmail.com
port = 587Then send a real message:
printf 'Subject: msmtp test\n\nIt works.\n' | msmtp you@gmail.comSilence 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-certificatesAdd 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.rcOlder 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.comWith -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 connectionRoute 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.shA 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=rootThe 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.servicesudo systemctl daemon-reload
sudo systemctl start status-email@backup.service # test it without waiting for a failureTroubleshooting
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=BadCredentialsGmail 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-certificatesTest 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-starttlsSMTP 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=WR2Nothing 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 blockedKeep 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 LOGINlines. - 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.
Related Content
- How to Fix Permission Denied (publickey) in SSH and GitHub covers the other credential setup every new server needs.
- Learn systemd explains the units and timers that generate these alerts.
References
- Sign in with app passwords, for creating and revoking app passwords.
- msmtp manual, for every configuration option, including
passwordeval. - GNU Mailutils manual, for the
mailcommand and its variables.

Comments #