Guides

Linux mail command: send email from the terminal with mail, sendmail, msmtp and swaks

The Linux mail command explained: send email from the Ubuntu terminal with mail or sendmail, relay through port 587 with msmtp, test with swaks.

Published ~9 min read
On this page

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.

Debian · Ubuntu · the short version
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.com

Install 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.

ToolWhat it doesDebian, Ubuntu package
mailSimple mail command, no attachmentsbsd-mailx
mailGNU version, with attachmentsmailutils
s-nailmailx with attachments; can also speak SMTP itselfs-nail
muttFull mail client in the terminalmutt
msmtpRelay client; msmtp-mta makes it the sendmailmsmtp, msmtp-mta
PostfixFull mail server with a queuepostfix
swaksSMTP test toolswaks

Which mail command do you have?

Several unrelated programs answer to mail, and their options differ. Ask the system which one it is:

any Linux
readlink -f "$(command -v mail)"
# /usr/bin/bsd-mailx  /usr/bin/mail.mailutils  or  /usr/bin/s-nail

On Rocky Linux and AlmaLinux 9, mail is s-nail. The options that differ:

Taskbsd-mailxGNU Mailutilss-nail
Subject-s-s-s
Sender address-r addr-r addr-r addr
Attach a fileNot supported-A file-a file
Add a header-a "Header: value"-a "Header: value"-C "Header: value"
Talk SMTP itselfNo, sendmail onlyThrough its mailer settingYes, 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.

  1. 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
  2. 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 : relay
    Debian · Ubuntu
    sudo chmod 600 /etc/msmtprc

    Mode 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 ~/.msmtprc with mode 600. Use a separate SMTP account or an app password for the server, so that a leak does not open your main mailbox; passwordeval instead of password reads it from a command. For port 465, set port 465 and tls_starttls off.

  3. 3Send mail for root to a real address
    /etc/aliases
    root: you@example.com
    default: you@example.com

    msmtp replaces local recipients such as root with these addresses; default catches every other local user. Without it, mail to root has no domain and the relay refuses it.

  4. 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-pager
    Example 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

any 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.txt

Typed 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

any Linux with an MTA or msmtp-mta
/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?

three ways
# 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.txt

bsd-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.

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.pdf

Without --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) or 550 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

Debian · Ubuntu
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 waiting

msmtp 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

MessageCauseFix
mail: command not foundNo mail command installedInstall msmtp-mta first, then bsd-mailx or mailutils
account default not found: no configuration file availablemsmtp found no config this user can readCreate /etc/msmtprc or ~/.msmtprc; check its owner and mode
authentication failed, server message 535 5.7.8Wrong user or password, or the provider wants an app passwordUse the account's SMTP credentials or an app password
530 5.7.0 Must issue a STARTTLS command firstTLS is off on port 587tls on and tls_starttls on
Connection timed out on 587 or 465Wrong host, or outbound traffic blockedTest with nc -vz -w 5 smtp.example.com 587
status=deferred with :25: Connection timed outOutbound port 25 is blockedRelay through port 587 instead
550 5.7.25 or 550 5.7.26 from GmailDirect delivery without PTR, SPF or DKIMRelay 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.

Related articles

All guides