One year with Notmuch

Email… One of the last bastions of non-proprietary communication with the freedom to choose both service providers and clients alike. Some call it archaic but none can avoid using it to some degree. For years I struggled to find an email client I actually liked. I tried all the usual suspects: Mozilla Thunderbird, mutt, alpine, and others. Even mu4e didn’t scratch that itch.

I’ve spent the last few years on Thunderbird as I made a point that even if everything else is failing, email must work, so I tried to avoid any unnecessary hackery. In the end my nature got better of me: about one year ago I switched to Notmuch and now I think I’m ready to write down my experience.

Spoilers

I’m not switching back, Notmuch is great.

Notmuch is at its core an email search engine. It indexes the mails and allows to query the resulting database. That’s all it does, there is no fetching and sending email or the UI apart from the CLI tool. For these things I needed separate tools.

I chose isync to fetch my email and msmtp to send it. At first glance it seems more complex than simply clicking away the settings in Thunderbird but I actually like it better. They are configured with plaintext and there is a very clear separation of concerns. The config files look like this:

~/.msmtprc

defaults
port 587
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt

account HomeAccount
host smtp.example.com
from vifon@example.com
auth on
user vifon@example.com
passwordeval pass mail/vifon@example.com

account WorkAccount
host mail.workplace.example.com
port 465
from vifon@workplace.example.com
auth on
tls_starttls off
user vifon@workplace.example.com
passwordeval pass mail/vifon@workplace.example.com

account Gmail
host smtp.gmail.com
from vifon@gmail.com
auth on
user vifon@gmail.com
passwordeval pass mail/vifon@gmail.com

account default : HomeAccount

~/.mbsyncrc

IMAPAccount HomeAccount
Host imap.example.com
User vifon@example.com
PassCmd "pass mail/vifon@example.com"
SSLType IMAPS

IMAPStore HomeAccount-remote
Account HomeAccount

MaildirStore HomeAccount-local
Subfolders Verbatim
Path ~/Mail/vifon@example.com/
Inbox ~/Mail/vifon@example.com/Inbox

Channel HomeAccount
Master :HomeAccount-remote:
Slave :HomeAccount-local:
Patterns *
Create Both
Expunge Both
SyncState *


IMAPAccount WorkAccount
Host mail.workplace.example.com
User vifon@workplace.example.com
PassCmd "pass mail/vifon@workplace.example.com"
SSLType IMAPS

IMAPStore WorkAccount-remote
Account WorkAccount

MaildirStore WorkAccount-local
Subfolders Verbatim
Path ~/Mail/vifon@workplace.example.com/
Inbox ~/Mail/vifon@workplace.example.com/Inbox

Channel WorkAccount
Master :WorkAccount-remote:
Slave :WorkAccount-local:
Patterns *
Create Both
Expunge Both
SyncState *


IMAPAccount Gmail
Host imap.gmail.com
User vifon@gmail.com
PassCmd "pass mail/vifon@gmail.com"
SSLType IMAPS

IMAPStore gmail-remote
Account Gmail

MaildirStore gmail-local
Subfolders Verbatim
Path ~/Mail/vifon@gmail.com/
Inbox ~/Mail/vifon@gmail.com/Inbox

Channel Gmail
Master :gmail-remote:
Slave :gmail-local:
Patterns *
Create Both
Expunge Both
SyncState *

If you don’t like the syntax (it’s not great but it does its job), don’t worry, there is little need to touch these files after the initial setup, unless you need to add or remove an account.

In both cases, I’m using pass(1) for the password storage instead of putting my passwords in plaintext there. I find pass excellent for automated access like this.

Summarizing: After fetching my inbox using isync, I have a full offline copy of all my mails at ~/Mail. Notmuch can then index it.

OK, we’re done with the internals. As for the UI, I chose notmuch.el. It’s based on GNU Emacs, so it’s right up my alley. There are other alternatives, including using Notmuch from mutt/neomutt or dedicated frontends like alot.

If you’re using Notmuch as a search engine for a “regular” email client like mutt, that’s about it. But I was going to use a client centered around Notmuch so the fun only begins. First of all, since it’s essentially a database, Notmuch has no concept of folders. Instead it makes extensive use of tags, similar to the ones known from Gmail but with a few very important differences. I didn’t like the Gmail tags because they wanted to be both tags and folders simultaneously. What do I mean? You needed to create them (one way or another) and they still existed even when empty. For me it felt like a pretty big cognitive overhead to create a new tag as it would linger for probably too long and pollute the namespace. In Notmuch the tag exists only as long as there are messages with this tag. The tags are inherently bound to messages and don’t exist as a separate concept. These are the main tags I use:

  • inbox (for non-archived mail, archiving consists on only removing this tag),
  • new (for the mail that appeared in the latest fetch and weren’t there before),
  • sent,
  • unread,
  • flagged,
  • actionable,
  • many less interesting ones.

Some of them are directly tied to the server-side flags (like unread or flagged), others exist solely in the Notmuch index and I assign them using the pre- and post-hooks that are being run before and after I refresh the mail index respectively. For instance, my pre-hook consists of:

#!/bin/bash -e
notmuch tag -new -- tag:new
mbsync -a || true

…so first I remove all the new tags from the previous run and then Notmuch runs mbsync for me to fetch all the new mail. I assign the new tags by specifying this option in ~/.notmuch-config:

[new]
tags=unread;inbox;new;

My post-hook is much more personalized, so there’s little point in posting it whole here, but that’s the place where one would add various automated tagging rules. I have there rules like this, that make my work more organized:

notmuch tag +flagged -- tag:new and query:work and '"Assignee: Wojciech Siewierski"'

In notmuch.el tagging is done by pressing +sometag to add some tag or -sometag to remove it. Yes, just a plus or minus and the tag. It can be “chained” before confirming with Enter like this: -inbox -unread +actionable -flagged. It feels really natural and doesn’t get in the way.

Considering Notmuch.el runs inside GNU Emacs, it allows to use all its text processing capabilities when composing mail. It really makes a difference for the Emacs power users and that alone is a major argument for using Notmuch for me. It utilizes the Emacs message-mode with some additions so most generic info about composing email in Emacs found online applies here. GPG signing, composing complex mail (like composing HTML mail with Markdown) or attaching files is pretty easy thanks to that. For instance it’s possible to use gnus-dired-mode created for the Gnus mail client (also Emacs-based) to select the attachments directly with the dired file manager (yes, you guessed), it doesn’t even need to know anything about Notmuch, it just knows about message-mode. What really surprised me, message-mode handles a regular drag’n’drop of the attachments from traditional graphical file managers, so if you’re leaning that way, more power to you!

I really came to like the way notmuch.el handles the HTML email. By default it prefers showing the plaintext version of email but if none is available or the user requests the HTML version it manually, it half-renders it. It’s good enough to handle the links and formatting but usually not complete enough to allow all the usual crap HTML mail are infamous for in some communities. But if I do need to render it fully, notmuch.el can save the HTML part of the mail in a temporary directory and open it with a web browser. At first it felt inconvenient but I came to prefer it to the regular approach: I’ll probably need to open a web browser to open some links in this mail either way, so I get a head start here.

When it comes to the regular email UI elements, notmuch.el is more than competent. It can display the mail list both as threads (one thread == one list element) and as trees (one thread == one tree, one mail == one tree node) and the thread navigation is top tier.

Is Notmuch + notmuch.el perfect? Of course not. My main “complaint” is the disconnect between the abstract tags system and the actual mailbox structure. I can include the mail folder hierarchy in a Notmuch search query but that’s it. When I want to know the actual way my mail are stored, I either inspect the ~/Mail directory manually or just start Thunderbird which I’m still keeping as my fallback (remember? Email must work!). Additionally, the way I use email is very centralized. The Notmuch metadata is stored locally, so using it on more than one device is tricky. I have a single machine I run Notmuch on so I didn’t have a chance to try muchsync but it’s definitely more work than just logging in via IMAP and being done with it. I have a few rules to handle the Gmail mail I archived on my mobile devices:

notmuch tag -inbox -- tag:inbox and not folder:/Inbox/ and path:/@gmail.com/

…and I don’t bother making Gmail aware of mail being archived in Notmuch.

Many will miss the lack of automatic mail checking out of the box but for me it’s an improvement. I prefer to check my mail on my own schedule, not run to check it just because a notification came.

That’s roughly how I’ve been using Notmuch for the last year. All in all, it’s a trade-off I’m willing to accept but it’s not for everyone. I don’t see myself switching mail clients anytime soon. Notmuch is the best way I found to organize and search my mail, and it allows me to utilize my editor of choice. Even if you’re not a fan of Emacs, it’s worth a try with one of the other frontends, I know some happy users that did.

blogroll

social