Short answer: to send email from the Linux terminal, pipe a body into the mail command: echo "Backup done" | mail -s "Backup" you@example.com. The mail command only hands the message to /usr/sbin/sendmail, so the server also needs something that delivers it. On a VPS that is usually msmtp, relaying through an SMTP account on port 587.
sudo apt install -y msmtp msmtp-mta # a sendmail that relays through your SMTP account
sudo apt install -y bsd-mailx # the mail command; msmtp-mta already covers its MTA dependency
sudoedit /etc/msmtprc # account details, see below
echo "Backup finished at $(date)" | mail -s "Backup OK" you@example.comInstall msmtp-mta first. bsd-mailx depends on a mail transfer agent and Mailutils recommends one; with nothing in place, apt picks the distribution’s default, which is a full mail server (Exim on Debian, Postfix on Ubuntu). This page is written from the man pages of bsd-mailx, GNU Mailutils, s-nail, msmtp and swaks on manpages.debian.org, Debian’s package data and Google’s sender guidelines, as of October 2026.
How mail leaves a Linux server
Three layers do the work. A program writes the message: mail, mutt or your script. It hands the message to /usr/sbin/sendmail, the interface every Unix mail program expects. Whatever provides that interface delivers it, either a full mail transfer agent (Postfix, Exim) that delivers on its own and queues and retries, or a relay client such as msmtp that passes everything to one SMTP server you log in to.
| Tool | What it does | Debian, Ubuntu package |
|---|---|---|
mail | Simple mail command, no attachments | bsd-mailx |
mail | GNU version, with attachments | mailutils |
s-nail | mailx with attachments; can also speak SMTP itself | s-nail |
mutt | Full mail client in the terminal | mutt |
msmtp | Relay client; msmtp-mta makes it the sendmail | msmtp, msmtp-mta |
| Postfix | Full mail server with a queue | postfix |
swaks | SMTP test tool | swaks |
Which mail command do you have?
Several unrelated programs answer to mail, and their options differ. Ask the system which one it is:
readlink -f "$(command -v mail)"
# /usr/bin/bsd-mailx /usr/bin/mail.mailutils or /usr/bin/s-nailOn Rocky Linux and AlmaLinux 9, mail is s-nail. The options that differ:
| Task | bsd-mailx | GNU Mailutils | s-nail |
|---|---|---|---|
| Subject | -s | -s | -s |
| Sender address | -r addr | -r addr | -r addr |
| Attach a file | Not supported | -A file | -a file |
| Add a header | -a "Header: value" | -a "Header: value" | -C "Header: value" |
| Talk SMTP itself | No, sendmail only | Through its mailer setting | Yes, the mta variable |
Relay through an SMTP account with msmtp
A server that only sends (cron reports, alerts, a script’s mails) does not need a mail server of its own. msmtp passes each message to one SMTP server where you have an account: your mail provider, a transactional mail service or your own mail server, on submission port 587 with STARTTLS and a login. That server’s reputation and DKIM signature then carry your mail, and the VPS’s own IP address never talks to the recipient. Which port to use is covered in SMTP ports.
- 1Install msmtp as the system's sendmail
Debian · Ubuntu sudo apt install -y msmtp msmtp-mta ls -l /usr/sbin/sendmail # now points to msmtp - 2Write /etc/msmtprc
/etc/msmtprc defaults auth on tls on tls_starttls on syslog LOG_MAIL aliases /etc/aliases account relay host smtp.example.com port 587 from server1@example.com user server1@example.com password an-app-password-for-this-server account default : relayDebian · Ubuntu sudo chmod 600 /etc/msmtprcMode 600 lets only root read the password, which suits a server where cron jobs, fail2ban and your scripts run as root. Another user who needs to send gets its own
~/.msmtprcwith mode 600. Use a separate SMTP account or an app password for the server, so that a leak does not open your main mailbox;passwordevalinstead ofpasswordreads it from a command. For port 465, setport 465andtls_starttls off. - 3Send mail for root to a real address
/etc/aliases root: you@example.com default: you@example.commsmtp replaces local recipients such as
rootwith these addresses;defaultcatches every other local user. Without it, mail toroothas no domain and the relay refuses it. - 4Test it
Debian · Ubuntu printf 'Subject: msmtp test\n\nHello from %s\n' "$(hostname)" | msmtp you@example.com echo "Body text" | mail -s "mail through msmtp" you@example.com journalctl -t msmtp -n 5 --no-pagerExample log line host=smtp.example.com tls=on auth=on user=server1@example.com from=server1@example.com recipients=you@example.com mailsize=312 smtpstatus=250 smtpmsg='250 2.0.0 Ok: queued as 4dXyz' exitcode=EX_OK
smtpstatus=250 means the relay accepted the message. msmtp --serverinfo --host=smtp.example.com --port=587 --tls=on --tls-starttls=on shows what a server offers before you write a config, and msmtp -d prints the whole SMTP dialogue (the man page warns that this output can include your password). Cron uses the same /usr/sbin/sendmail, so MAILTO in a crontab now reaches you as well.
Sending with the mail command
echo "Disk on web-01 is at 91%" | mail -s "Disk warning" you@example.com
mail -s "Nightly report" you@example.com < /var/log/nightly-report.txt
df -h | mail -s "df on $(hostname)" you@example.com
mail -s "From the right sender" -r server1@example.com you@example.com < body.txtTyped without input, mail you@example.com asks for a subject and then reads the body until you press Ctrl+D on an empty line. Many relays reject a sender address that does not belong to the account you log in with, so leave -r out unless the address matches, and let msmtp’s from line set it.
The sendmail interface: full control over headers
/usr/sbin/sendmail -t <<'EOF'
To: you@example.com
From: server1@example.com
Subject: Certificate renewal failed on web-01
Content-Type: text/plain; charset=UTF-8
certbot renew exited with code 1. See /var/log/letsencrypt/letsencrypt.log.
EOF-t takes the recipients from the To, Cc and Bcc headers, and the empty line ends the headers. Postfix, Exim and msmtp-mta all accept this form, which makes it the most portable way to send from a script, and it sets any header you like without a mail command installed.
How do you send an attachment from the command line?
# GNU Mailutils
mail -s "Weekly report" -A /srv/reports/week42.pdf you@example.com < body.txt
# s-nail
s-nail -s "Weekly report" -a /srv/reports/week42.pdf you@example.com < body.txt
# mutt: the double hyphen ends the list of attachments before the recipients
mutt -s "Weekly report" -a /srv/reports/week42.pdf -- you@example.com < body.txtbsd-mailx cannot attach files: it has no MIME support, and its -a only adds a header. mutt sends through /usr/sbin/sendmail unless you configure otherwise, so with msmtp-mta installed it uses the same relay. Your provider’s message size limit applies to the encoded attachment, which is about a third larger than the file.
Test an SMTP server with swaks
swaks talks SMTP directly and prints every step, so it shows whether the connection, TLS, the login or the recipient is the problem, without msmtp or a mail server in between. Install it with sudo apt install swaks.
# port 587 with STARTTLS and a login; asks for the password
swaks --to you@example.com --from server1@example.com \
--server smtp.example.com:587 --tls \
--auth LOGIN --auth-user server1@example.com --protect-prompt
# port 465: TLS from the first byte
swaks --to you@example.com --server smtp.example.com:465 --tls-on-connect \
--auth --auth-user server1@example.com
# with an attachment (the @ marks a file name)
swaks --to you@example.com --server smtp.example.com:587 --tls \
--auth --auth-user server1@example.com --attach @report.pdfWithout --auth-password, swaks asks for the password, and --protect-prompt hides what you type. --tls insists on STARTTLS and stops if the server does not offer it. The test message has the subject “test” with the date and the body “This is a test mailing”. To read a server’s raw replies with openssl s_client instead, see testing an SMTP port.
Why mail sent straight from a VPS lands in spam or bounces
Without a relay, a mail server on the VPS delivers directly: it looks up the recipient’s MX record and connects to it on port 25. Two things get in the way.
- Outbound port 25 is often blocked. Home providers and many cloud networks block it by default, so direct delivery times out while port 587 to a relay works. Port 25 explained has the test. Before you run a mail server on a VMHeaven server, read the Acceptable Use Policy and talk to support first.
- Receivers check who is sending. Gmail’s sender guidelines (as of October 2026) require every sender to pass SPF or DKIM, to send from an IP address with valid forward and reverse DNS, and to use TLS; senders of more than 5,000 messages a day to Gmail need SPF, DKIM and DMARC together. A fresh server IP with none of these goes to spam or is refused, for example with
550 5.7.25(no PTR record) or550 5.7.26(unauthenticated sender).
A server that sends directly therefore needs an SPF record that lists its address (the SPF record generator builds one), DKIM signing (OpenDKIM next to Postfix, for example), a DMARC record, and a PTR record whose name resolves back to the same IP, as reverse DNS and PTR records explains. On a VMHeaven VPS you set the PTR yourself in the panel; changes take about 30 minutes to show. The mail server check then reads MX, SPF, DKIM, DMARC and reverse DNS for your domain in one report. If you relay through a provider instead, its servers bring the PTR and the DKIM signature, and your SPF record only needs the include the provider names.
Did it go out? Checking the delivery logs
journalctl -t msmtp -n 20 --no-pager # msmtp with "syslog LOG_MAIL"
sudo journalctl -t postfix/smtp -n 20 --no-pager # Postfix deliveries
sudo grep -E 'status=(deferred|bounced)' /var/log/mail.log # where rsyslog writes mail.log
mailq # Postfix: what is still waitingmsmtp has no queue. It either hands the message to the relay, logged as smtpstatus=250, or fails at once with an error and a non-zero exit code, which the calling program sees. Whatever happens after the relay accepted it shows up in your provider’s logs or as a bounce in the sender’s mailbox. Postfix queues and retries: each attempt ends in status=sent, status=deferred (temporary, tried again later) or status=bounced (final), with the receiving server’s answer in brackets. sudo postqueue -f retries the queue now.
Common errors and fixes
| Message | Cause | Fix |
|---|---|---|
mail: command not found | No mail command installed | Install msmtp-mta first, then bsd-mailx or mailutils |
account default not found: no configuration file available | msmtp found no config this user can read | Create /etc/msmtprc or ~/.msmtprc; check its owner and mode |
authentication failed, server message 535 5.7.8 | Wrong user or password, or the provider wants an app password | Use the account's SMTP credentials or an app password |
530 5.7.0 Must issue a STARTTLS command first | TLS is off on port 587 | tls on and tls_starttls on |
| Connection timed out on 587 or 465 | Wrong host, or outbound traffic blocked | Test with nc -vz -w 5 smtp.example.com 587 |
status=deferred with :25: Connection timed out | Outbound port 25 is blocked | Relay through port 587 instead |
550 5.7.25 or 550 5.7.26 from Gmail | Direct delivery without PTR, SPF or DKIM | Relay through a provider, or set up the DNS records above |
Mail from a VPS without the trouble
For alerts and reports from a VPS or a dedicated server, the relay is the quiet route: msmtp to an SMTP account on port 587, and the server’s IP never has to be a mail server. If you do run your own, check the new address with the blacklist check before the first mail goes out. Unsolicited bulk mail is against the Acceptable Use Policy, and activity that gets an address range onto a blocklist can end the service.
Frequently asked
How do I send an email from the Linux command line?
Pipe the body into the mail command: echo 'Body' | mail -s 'Subject' you@example.com. The mail command needs a working /usr/sbin/sendmail behind it. On a server that only sends, install msmtp and msmtp-mta and put an SMTP account on port 587 into /etc/msmtprc, so that msmtp relays the message through that account.
How do I send an attachment with the mail command?
It depends on which mail you have. GNU Mailutils attaches with '-A file', s-nail with '-a file', and bsd-mailx cannot attach files at all (its -a adds a header). mutt also attaches with -a wherever it is installed. 'readlink -f $(command -v mail)' shows which mail command the system uses.
Why do emails sent from my VPS go to spam?
Receivers check who is sending. Gmail, for example, requires SPF or DKIM from every sender and valid forward and reverse DNS (a PTR record) for the sending IP, and many networks block outbound port 25 anyway. Either relay through an SMTP provider on port 587, or give the server SPF, DKIM, DMARC and a PTR record that resolves back to its IP.
What is the difference between mail, sendmail and msmtp?
mail is a user program that writes a message. sendmail, at /usr/sbin/sendmail, is the standard interface that programs hand messages to. msmtp is one program that can provide that interface: it passes every message to a single SMTP server you log in to, without a queue. Postfix and Exim provide the same interface as full mail servers that queue and retry.