It has been 18 months that I read & write my emails in Emacs. No need to say I have enjoyed
the mouse-free experience brought by Emacs. Recently, I had to keep track a new
email account. So I came across my old note written back then which I enhanced
in this post. I made lots of updates subsequently including password management
pass, multi-accounts support etc. to make it as complete as possible.
A full back-and-forth cycle of email consists to
- Receive email through a program which synchronize emails locally from an email server.
- Read email through a program (MUA) whose the UI offers an organized & handy presentation of emails.
- Compose email in whatever editor.
- Send email with a mail transfer agent or an interface of it.
I made the following choices which I will detail the configuration throughout this post:
- Receive email: offlineimap.
- Read email: notmuch.el.
- Compose email: Emacs message mode.
- Send email: smtpmail-multi to send email through multiple SMTP servers.
As you may have seen, except the reception of email, the remaining can be done within Emacs.
As stated above, we use
offlineimap to fetch emails from potentially multiple
mailbox. But most importantly, we store all emails locally for two purposes:
1. to be able to read emails offline, 2. to not mess up tags synchronization
which may cause data loss. You may think that your huge mailbox would take a
tremendous place in your disk. Well, I can say that if you pay attention to
keep only one copy of your emails1, it should not take much. For instance,
I have 2.6k emails taking 460MB of the disk.
offlineimap with your favorite package manager2. Then copy
the minimal configuration (the path depends on your distribution).
Here is the relevant part of my configuration (
~/.offlineimaprc) for reference.
See the documentation in
or Archwiki for more information. Note the
postsynchook option at the account
level: it’s an email tagging script which is run as soon as new email arrives. We
will come soon to its content in this section. Remember what I said regarding
the space taken by locally stored email? Well, they remain tiny provided that
they are not duplicated elsewhere. That’s not always the case, for instance
Gmail may store an email in the folder
You may consider to filter out the extra folders you don’t want as below with a
python’s lambda expression or a function (see the documentation).
Launch offlineimap automatically at boot
You would certainly want to launch automatically
offlineimap at boot.
This can be done with
systemd. In my case, I have three accounts, it’s
advised 3 to create three separated
systemd services and set
maxsyncaccounts = 1 in
~/.offlineimaprc as we have done above.
Instead of write three different system service files, I write the following
template unit file where the variable
%i will match later with an account
.offlineimaprc. (Note that you should avoid “@” in the account name
systemd gives it a precise meaning).
systemctl daemon-reload to load the new service file.
The following commands enable the auto-start on boot and launch the service right now.
account-* is the account name appeared in each
[ Account XXX ] section.
If everything goes well,
offlineimap will sync emails on the next boot automatically.
Auto-tagging with notmuch
The next thing to do is email auto-tagging, without this feature your mailbox will be a nightmare. Again, install notmuch with your favorite package manager. We will write the script aforementioned so that email be filtered as soon as they are synced locally.
Before starting to use notmuch, you must configure it. In particular, you have to set the
database path, your email accounts which appeared in
tagging rule for incoming email and tag to exclude by default when searching.
Expose highly active addresses
The following command lists email-senders address sorted by decreasing amounts of emails sent4.
To to expose highly active mailing list.
The snippet above help us to find out the best contributors of our inbox to tag them properly.
Here is my little shell script
~/.email/postsync.sh which is run once
finished to sync my emails. You might want to take a look at
notmuch help search-terms
to understand the syntax of tagging commands.
I identify several visibility categories of emails:
- I don’t want to see them at all and they’re harmful => spam
- I don’t want to see them at all => blacklisted
- I want to see them but it doesn’t matter when => move out inbox
- It’s important! => keep them in the inbox and tag them more
Follow the instructions given on the official website.
The key-bindings I use are from evil-collection. They are quite different from the default ones.
You can define new keybindings for different notmuch views (
message) as below, but usually I rarely tag manually an email (except flagging important one).
Instead, I add a new tagging rule as depicted above.
C-x m (
compose-mail) in Emacs to compose an email to send. Normally, the
From:=/=To: fields can be autocompleted.
Unless your local system is configured for sending email using sendmail, you may want to access a remote SMTP server.
Below is a fragment of my SMTP setup. You should acquire this information from
the host (Gmail5, your institution, your company etc.). Using
smtpmail is not
enough to sending email with different accounts. Fortunately, the package
smtpmail-multi made the task easier.
Then you have to put your credentials somewhere. Such places are designated by
auth-sources which defaults to
("~/.authinfo" "~/.authinfo.gpg" "~/.netrc").
For instance, put the following in
Patch: Fully-Qualified Domain Name (FQDN)
You may encounter issue regarding the FQDN when sending email. I have the following patch in my configuration coming from here.
Bonus: passwords encryption with pass
You may have seen a security hole which would hopefully make you uncomfortable: we have written credentials in plain text. Let’s fix it. I assume in the following that the reader has already setup gpg (2.1+) and pass.
Remember, we have stored passwords in
~/.offlineimaprc to pull emails locally
offlineimap and in
~/.authinfo so that Emacs is able to send email.
Create a password for your email account.
pass insert email/myaccount
Create a python function that retrieves the password (in
1 2 3 4 5
#! /usr/bin/env python3 from subprocess import check_output def get_pass(account): return check_output("pass email/" + account, shell=True).splitlines()
.offlineimaprc, under the general section, indicate the python file
1 2 3
[general] # ... pythonfile = ~/.offlineimap/pass.py
and replace each
remotepass = passwordby the next one.
remotepasseval = get_pass("myaccount")
To make Emacs read credentials through
pass, we use the package
auth-source-pass which exactly do the job for us. The configuration is simple.
You should create a
<smtp host>.gpg with
pass --edit email/<smtp host> for
each smtp server. For instance, the entry of
Cache gpg passphrase
offlineimap and Emacs will retrieve your passwords through
pass. Great! But, if you setup
gpg without extra configuration, you will be
prompted the passphrase every two hours. Why? The answer lies in the gpg agent
--max-cache-ttl. The documentation says
That is, by default, the passphrase is cached 10 minutes and can be extended
each time it is accessed up to 2 hours. As we set
~/.config/systemd/user/offlineimap@.service, it ensures that we reach the
maximum cache time. To increase the cache time permanently to one day, add the
line below in
You should restart
gpg-agent to see the effect (
gpg -K should be enough).
At this point, not only are your passwords secure to some extent, but no one can
see and write emails on your behalf after one day without enter the passphrase.
Addendum: general workflow
I summarize below how one maintains this email workflow.
Tagging emails. Update
~/.email/postsync.shwhen necessary (usually to blacklist some addresses).
Changing password. Modify adequately
.authinfo, or if you use
passas above, update the passwords with
pass edit email/<account>. Beware, if you only change your password remotely, you won’t be able to receive and (possibly) write any email.
Adding new email. Each time you want to add a new email account in you workflow, you should
accounts, add one more account section plus
associated local/remote sections;
smtpmail-multi-associationsif that account may be used to write email;
- update credentials with
systemctl enable --user --now offlineimap@ACCOUNT-NAME.service;
- (optional) update
postsync.shto tag the email written by yourself.
After setting up
offlineimapcorrectly, you can check this information by running
fdupes -mr .under
Technically you can aggregate all duplicate email addresses with
jq, but you would have to handle the case, comma-separated addresses, and the
"First Last <email@example.com>"notation. It’s merely an example after all. ↩︎
If you retrieve your emails at a frequency lower than every 10 minutes, then you should also assign the same value of