1. Introduction

As one of the oldest still used forms of electronic message exchange, mails (emails, e-mails) emulate the real-world postal services, sometimes called snail mail. This is not much different from the internal Linux mailing system.

In this tutorial, we explore the Linux built-in mail exchange mechanism. First, we do a general overview of the internal Linux mail. Next, we continue with configuration. After that, we go through the mail directories, files, and formats. Then, we send some emails. Later, we understand how automatic mail checking works. Finally, we read the emails we sent.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.

2. Internal Linux Mail Overview

To begin with, the mailing mechanism is in place for many Linux distributions out of the box. This doesn’t mean every system is a global mail server. To have that, we require special packages and configuration, as well as the use of protocols like POP3 and IMAP.

Here, we only focus on mail processing within the system, not external network interaction.

2.1. Features

Similar to traditional electronic mail, Linux offers a message exchange system with many benefits:

  • mail exchange
  • authentication
  • authorization
  • separate inboxes
  • forwarding
  • notifications

Still, almost every Linux installation can internally route mails to users:

+-----------------------------+
|              | Mail | Users |
|              |------|-------|
|              | +--->| user1 |
|              | |    |-------|
|    System    | +---<| user2 |
|              |      |-------|
|              |      |  ...  |
|              |      |-------|
|              |      | userN |
+-----------------------------+

In fact, this is roughly equivalent to sending mail from and to the same domain of a public email service.

2.2. Toolset

To facilitate the actual message exchange activities, Linux distributions can provide different commands and tools such as mutt and others.

Most often, we have the GNU mail command:

  • check for mail
  • compose mail
  • send mail (including attachments)
  • handle configuration

In general, this utility has evolved from the initial UNIX and BSD versions to the GNU implementation from the mailutils package that most installations use today.

In addition to mail, the ubiquitous mailutils provides several other tools:

  • messages: get number of mails within a provided or system mailbox directory
  • frm, from: get mail headers from a mailbox
  • movemail: transfer mail to local file
  • readmsg: extract (filtered) messages from a directory
  • decodemail: decode multipart messages

Hence, we use the package for this article. Now, let’s go over some typical scenarios.

3. Linux Mail Configuration (Optional)

The main global configuration file for Linux mail as implemented by mailutils is typically /etc/mailutils.conf:

$ cat /etc/mailutils.conf
address {
  email-domain gerganov.com;
};

Here, we only specify what automatic email address domain name we’d like.

In addition, we have the /etc/mail.rc RC file with commands.

On the other hand, we can also have a local user configuration in $HOME/.mail and user commands in $HOME/.mailrc.

In these files, we can control many mail aspects:

The configuration can employ variables and has a fairly simple syntax with a comprehensive set of options.

4. Linux Mail Directories and Format

By default, there are two main directories responsible for keeping and preserving emails:

  • /var/spool/mail/<USERNAME>: classic location for the mail inbox (file) spool of user USERNAME
  • /var/mail/<USERNAME>: modern location for the mail inbox (file) spool of user USERNAME
  • $HOME/mbox: saved messages mailbox

Usually, /var/spool/mail is a symbolic link to /var/mail. We can check this via realpath:

$ realpath /var/spool/mail
/var/mail

In any case, all messages are in the MBOX format:

$ cat $HOME/mbox
From SENDER@domain Wed Nov 22 10:06:56 2023
Return-path: <SENDER@domain>
Envelope-to: RECIPIENT@domain
Delivery-date: Wed, 22 Nov 2023 10:06:56
Received: from USERNAME by DOMAIN with local (Exim 4.94.2)
        (envelope-from <SENDER@domain>)
        id 1r7uR1-000Jqc-EU; Wed, 22 Nov 2023 10:06:56
Subject: Mail Subject
To: <RECIPIENT@domain>
X-Mailer: mail (GNU Mailutils 3.10)
Message-Id: <E1r6661-000Jqc-DD@domain>
From: SENDER@domain
Date: Wed, 22 Nov 2023 10:06:56

Mail Body.

In short, this is a basic text format with mail fields such as From, Subject:, To:, and similar, as defined in RFC 2074 – Common Internet Message Headers.

5. Send Linux Mail

Of course, we can send emails via the mail command:

$ whoami
user2
$ mail -s 'Mail Subject' user1 <<EOI
Mail body.
EOI

First, we use whoami to verify the current user is user2. After that, we invoke mail and set the [-s]ubject as Mail Subject and the intended recipient as user1. For the main body, we use a here-string with the EOI terminator.

Alternatively, we can compose our message interactively:

$ mail user1
Cc: [Return]
Subject: Interactive Message[Return]
Write message body here and press Ctrl+D (EOF) to submit.[Return]
[Return]
This way, messages can be on multiple lines.[Return]
[Ctrl+D]

Notably, we start by entering any Cc: recipients and the mail Subject:. Then, we continue with the mail body until Ctrl+D (EOF) is pressed.

At this point, we’ve sent two emails from user2 to user1.

Let’s send a message to several recipients simultaneously:

$ mail -s 'Multimail' user1,user3 <<EOI
Body.
EOI

Now, user1 should have three messages in their mailbox.

6. Check Linux Mail

In most environments, the login and shell-spawn events trigger a mail check. This is usually done via configuration files.

However, modern shells often facilitate this via a feature called mailcheck, responsible for polling a preconfigured path of mail files at certain times.

6.1. Automatic Bash Mail Check

When it comes to Bash, mail checks are automated and controlled by several variables:

  • MAIL (default /var/mail/$(whoami)): path to current user mail inbox file
  • MAILPATH (default /var/mail/$(whoami)): path to current user mail inbox file
  • MAILCHECK (default 60): interval (seconds) between mail checks

Mail checks are done just before each primary prompt refresh. To disable interval mail checking, we can assign $MAILCHECK a non-integer non-zero value or just unset it.

Now, let’s see where the mail variables and other triggers are potentially set.

6.2. PAM Modules

In addition to the check at regular intervals, the pam_mail Pluggable Authentication Module (PAM) ensures we see notifications at certain login events.

In particular, three files often hold lines that configure mail initialization:

  • /etc/pam.d/login: standard (imply empty), meaning set $MAIL, show a You have… format message, and don’t show the spool
  • /etc/pam.d/sshd: standard, but with noenv, i.e., without setting $MAIL
  • /etc/pam.d/su: nopen sets $MAIL without any notifications

So, we can check the regular login setting:

$ cat /etc/pam.d/login
# Prints the status of the user's mailbox upon successful login
# (Replaces the `MAIL_CHECK_ENAB' option from login.defs).
#
# This also defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
session    optional   pam_mail.so standard

Notably, the $MAIL variable isn’t set for SSH sessions:

$ cat /etc/pam.d/sshd
# Print the status of the user's mailbox upon successful login.
session    optional     pam_mail.so standard noenv # [1]

Finally, using su doesn’t produce any notifications:

$ cat /etc/pam.d/su
# Defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
#
# "nopen" stands to avoid reporting new mail when su'ing to another user
session    optional   pam_mail.so nopen

To disable any of these behaviors, we can comment out the respective line.

6.3. Mail Check Behavior

First, we can check the login notification:

xost login: user1
Password: 
[...]
You have mail.
$ echo $MAIL
/var/mail/user1

Next, we see the example notifications after su:

$ whoami
user2
$ mail -s 'Mail 1' user1 <<< 'Mail body.'
$ su user1
$ echo $MAIL
/var/mail/user1
$ [wait 60 seconds] [Return]
You have new mail in /var/mail/user1
$

Also, let’s try an SSH session:

$ ssh user1@xost
[...]
You have new mail.
Last login: Wed Nov 22 11:10:00 2023
$ echo $MAIL

$

Finally, we can read these emails.

7. Read Linux Mail

While the rudimentary messages, readmsg, from (frm), and similar utilities are convenient for automation, mail is usually the preferred choice when it comes to user mail interaction:

$ mail
"/var/mail/user1": 3 messages 3 new
>N   1 user2@xost         Wed Nov 22 11:11  15/430   Mail Subject
 N   2 user2@xost         Wed Nov 22 11:11  17/530   Interactive Message
 N   3 user2@xost         Wed Nov 22 11:11  14/419   Multimail
?

Here, we see our user mail inbox at /var/mail/user1 and the three emails from earlier in several columns:

  1. selection (>)
  2. status ([N]ew, [P]reserved, [U]nread, [*] saved or written)
  3. number
  4. sender
  5. date and time (received)
  6. size (lines/characters, including headers)
  7. subject

Now, let’s read each message by supplying its number at the ? prompt:

? 1
Return-path: <user2@xost>
Envelope-to: user1@xost
Delivery-date: Wed, 22 Nov 2023 11:11:11 -0600
Received: from user2 by xost with local (Exim 4.94.2)
        (envelope-from <user2@xost>)
        id 1r80gN-00666y-Ke
        for user1@xost; Wed, 22 Nov 2023 11:11:11 -0600
Subject: Mail Subject
To: <user1@xost>
X-Mailer: mail (GNU Mailutils 3.10)
Message-Id: <E1r80gN-00666y-Ke@xost>
From: user2@xost
Date: Wed, 22 Nov 2023 11:11:11 -0600

Mail body.
? 2
[...]
Subject: Interactive Message
[...]

Write message body here and press Ctrl+D (EOF) to submit.

This way, messages can be on multiple lines.
? 3
[...]
Subject: Multimail
[...]

Body.
?

At this point, we can q[uit], so our messages automatically get saved in our home folder mbox file:

? quit
Saved 3 messages in /home/user1/mbox
Held 0 messages in /var/mail/user1

Thus, we can use mail –file with /home/user1/mbox and browse saved messages.

Should we use e[xit] or x[it] instead, no messages are moved to the user mbox. However, since spools are meant to be temporary, it’s usually a good idea to move messages to a more permanent location.

Of course, mail has many other features and a useful help system.

8. Summary

In this article, we talked about the mail system of Linux.

In conclusion, even though there are alternatives to mail, mailutils is usually preinstalled on many Linux distributions.

Comments are closed on this article!