Servers need to send email too. A backup script wants to report that it finished, a cron job should tell you when it fails, a monitoring check needs to raise an alert and a certificate renewal hook should confirm that it ran. Running a full mail server on every machine for this is more work than it is worth and a poor idea for deliverability: a server with no reputation, no reverse DNS and a blocked port 25 sends mail that is often rejected or lost. A better design is to keep the server simple and have it hand every message to an authenticated SMTP relay. This guide shows three ways to do that from the command line: msmtp, a small sender that behaves like the classic sendmail program; the mail command on top of it; and curl, which speaks SMTP without any extra software. It then covers cron and systemd alerts, how to keep the password safe and how to troubleshoot. The examples use placeholders. Take real values from your SMTP provider's dashboard.
Why relay instead of sending directly
When a server delivers mail itself, the receiving server judges the machine: its IP address, whether the reverse DNS name matches, whether the sending domain has SPF and DKIM and whether the IP has been abused before. Cloud and home networks frequently block outbound port 25 and have addresses that are on shared ranges mailbox providers distrust. A relay turns this around. Your server makes one authenticated, encrypted connection to a service that has a clean reputation and proper authentication, and the service delivers the message. You also get logs, bounce handling and a single place to rotate credentials. If a Postfix installation is already in place, the guide to configuring Postfix as an authenticated relay client is the heavier but more featureful alternative.
Install msmtp
# Debian and Ubuntu
sudo apt install msmtp msmtp-mta ca-certificates
# RHEL, Rocky, Alma and Fedora
sudo dnf install msmtpThe msmtp-mta package on Debian and Ubuntu creates a sendmail compatibility link, so programs that call /usr/sbin/sendmail, including cron, PHP and many monitoring tools, use msmtp automatically. Only install it on a machine that does not already run a mail server, because it takes over the sendmail name.
Write the configuration
msmtp reads /etc/msmtprc for the whole system or ~/.msmtprc for one user. The file holds the relay settings and, unless you store the password elsewhere, the password, so its permissions matter.
# /etc/msmtprc
defaults
auth on
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account relay
host smtp.example.com
port 587
from [email protected]
user your-smtp-user
passwordeval "cat /etc/msmtp-password"
account default : relaySet tls_starttls on with port 587. For implicit TLS on port 465, use port 465 and tls_starttls off. msmtp verifies the server certificate against the trust file, which is the setting that prevents someone on the network from reading your password; do not turn verification off to make an error disappear. The passwordeval line runs a command and uses its output as the password, so the password is not written in the main file and can come from a secrets tool instead. If you are unsure which port to choose, read which SMTP port to use.
sudo sh -c 'printf "%s" "your-smtp-password" > /etc/msmtp-password'
sudo chmod 600 /etc/msmtp-password /etc/msmtprc
sudo chown root:root /etc/msmtp-password /etc/msmtprcmsmtp refuses to read a configuration file that other users can read when it contains a password, which is a useful safeguard. If non-root users, such as a web server user, must send mail, create a group for them and give the file to that group with mode 640, or use a separate file per user.
Send a first message
printf 'To: [email protected]\nFrom: [email protected]\nSubject: Test from server\n\nHello from the command line.\n' \
| msmtp -a default [email protected]msmtp does not build headers for you: it sends what it reads from standard input. The envelope recipient is the address after the options, and the To, From and Subject lines are part of the message. Keep a blank line between the headers and the body. Add --debug to see the full conversation, including the TLS negotiation, and remove it afterwards because the output contains encoded credentials.
Use the mail command
Most administrators prefer the familiar mail command. Install a mail user agent such as s-nail or bsd-mailx, which calls sendmail and therefore msmtp.
echo "Backup finished: 42 GB in 18 minutes" \
| mail -s "Backup report: web01" -r [email protected] [email protected]
# with an attachment (s-nail)
echo "Report attached" | mail -s "Weekly report" -a report.csv [email protected]The -r option sets the sender, which must be an address your provider allows. If the From address is not on a verified domain, the relay will reject the message. See the guide on authenticating your sending domain.
Send with curl
If you do not want to install anything, curl can send a message over SMTP. Write the message to a file, including its headers, and give curl the envelope.
cat > /tmp/msg.txt <<'MSG'
From: Alerts <[email protected]>
To: [email protected]
Subject: Disk space warning
The /var volume on web01 is 91% full.
MSG
curl --ssl-reqd --url 'smtp://smtp.example.com:587' \
--mail-from '[email protected]' \
--mail-rcpt '[email protected]' \
--netrc-file /root/.netrc-smtp \
--upload-file /tmp/msg.txtThe --ssl-reqd option makes curl fail if STARTTLS is not available instead of falling back to clear text. For port 465, use smtps://smtp.example.com:465. Keep the credentials in a file given with --netrc-file, in the format machine smtp.example.com login USER password PASS with mode 600, and avoid --user USER:PASS on the command line: other users on the machine can see command-line arguments in the process list. The same applies to any tool: never put a password in an argument.
Cron and systemd alerts
Cron emails the output of a job to the user that owns it, using sendmail, which msmtp now provides. Set the recipient at the top of the crontab, and keep the job quiet when it succeeds, so that an email always means something.
[email protected]
# Mail only on failure: stdout hidden, errors are mailed
15 2 * * * /usr/local/bin/backup.sh >/dev/nullFor systemd services, add an OnFailure= unit that sends a message with the failed unit's name and the last log lines, which you can fetch with journalctl -u NAME -n 30. Alerts that arrive only when something breaks are far more useful than a daily report that everyone ignores. Add a second check that tells you if the expected success message has not arrived, because a dead cron daemon sends nothing at all.
Keep credentials safe
A server credential is only as safe as the weakest machine that holds it. Use a dedicated SMTP user for alerts, separate from the one your application uses, with the lowest limits that work, so a leak has a small blast radius. Keep the file mode at 600, do not commit it to a repository or a configuration-management template in plain text, and rotate the password if a person with access leaves. Avoid passwords in shell history and process lists. Restrict who may run the sending command if the machine is shared, since anyone who can run it can send mail as your domain.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
msmtp: authentication failed (method PLAIN) | Wrong user or password, or a trailing newline in the password file | Re-create the file with printf "%s"; see SMTP error 535 |
cannot connect or timeout | Outbound port blocked, or wrong host | Test the port from the server using the SMTP test guide |
TLS certificate verification failed | Missing CA bundle, wrong host name or the system clock is wrong | Install ca-certificates, use the exact host and check the time with timedatectl |
configuration file ... must have no more than user read/write permissions | File mode too open | Run chmod 600 on the file |
| Cron sends nothing | No sendmail on the path, or the job prints nothing | Check which sendmail and run the job by hand |
| Sender rejected | From address not on a verified domain | Use -r or from with an authenticated address |
| Message accepted but in spam | No SPF, DKIM or DMARC for the From domain | Publish the records and follow the spam checklist |
The msmtp log is the first place to look. Each attempt records the server reply, which tells you whether the problem is the connection, the login or the message. Combine it with the relay's own delivery logs to follow a message from your server to the recipient's domain.
Give your servers an authenticated relay Use a dedicated SMTP user for alerts and see every message in the delivery logs.
References and further reading
Frequently asked questions
How do I send email from a Linux script through SMTP?
Install msmtp, configure it with your relay host, port 587 with STARTTLS and your credentials, then pipe a message to msmtp or use the mail command.
Can curl send email?
Yes. Use curl with an smtp:// or smtps:// URL, --mail-from, --mail-rcpt and --upload-file with a message file, and add --ssl-reqd to require encryption.
How do I make cron email me only on failure?
Set MAILTO, send normal output to /dev/null and let errors on standard error be mailed. Also monitor for the success message not arriving.
Where should I keep the SMTP password on a server?
In a file readable only by the sending user, with mode 600, read by passwordeval or a netrc file. Never put it in command-line arguments.
Why does msmtp refuse my configuration file?
Because it contains a password and other users can read the file. Set the mode to 600 or 640 for a dedicated group.