Bassam Saeed

The Devil is in the Email, Mutt Guide Pt. 1
Jun 14, 2018

I’ve had many false starts with Mutt. It’s probably the piece of software I’ve had the most trouble getting into. Yes, more than Vim or Emacs. It’s not even Mutt’s fault really, I think the main culprit is that I always followed the examples and guides of people heavily focused on setting up an incredibly robust system with separate IMAP and SMTP setups. Some even add on custom filtering and tagging. It doesn’t help that many of these guides operate under the guise of a beginner’s guide.

I don’t want to sound like I’m condemning these excellent resources. There is nothing wrong with such a robust setup. It is after all the setup I wanted and one I eventually achieved after weeks of tinkering. Most of my problems getting started with Mutt are probably my own fault. The ArchWiki has an excellent starters guide but I’m probably too ambitious and jumped right ahead into trying to setup the fully featured, robust setup of my dreams and consequently would get a little confused and disheartened.

In this article I’ll be going through my personal journey setting up Mutt to meet my needs and explain my reasoning behind why I did certain things the way I did.

Table of Contents

I Started out with Nothing, and I Still Got Most of It Left

My initial setup is one where Mutt handles (to the best of its capabilities) everything I need. The robust setup with separate external IMAP, SMTP and tagging capabilities as well as a local copy of all my mail will come later. Got to start small.

The Beginnings

After having not touched Mutt for almost a year, this reddit post got me interested in trying Mutt again and building my own solution piece by piece just like I did in Vim. So I modified it slightly and this is what I started with in my $HOME/.mutt/muttrc:

set folder = "imaps://imap.gmail.com"

set imap_user = "bassam.saeed@gmail.com"
set imap_pass = `pass mail/gmail/mutt`

set smtp_url  = "smtps://bassam.saeed@gmail.com@smtp.gmail.com"
set smtp_pass = `pass mail/gmail/mutt`

set realname  = "Bassam Saeed"
set from      = "bassam.saeed@gmail.com"

set header_cache     = "~/.mutt/cache/headers"
set message_cachedir = "~/.mutt/cache/bodies"

set spoolfile = "+INBOX"
set postponed = "+[Gmail]/Drafts"
set trash     = "+[Gmail]/Trash"

This is a bare bones configuration but it should be enough to get most people at least started with Mutt.

Most of this should be fairly self-explanatory. The folder setting defines the default location of the mailbox. Because I’m not keeping a complete copy of all my mail locally, the setting is set to just use the Gmail IMAP server. The header and message caches are there so I don’t have to re-download them every time I start up Mutt. I consider the header cache pretty mandatory but some people may not need the message cache. The spoolfile is where unfiltered email arrives. In Gmail (and most email providers) this is known as the INBOX so you can define that relative to the default mailbox by putting a + in front. The postponed and trash settings are defined under [Gmail]. Other email providers will have it somewhere else.

I use the excellent pass utility as my password manager and so I allow Mutt to run this command for IMAP and SMTP authentication by using grave accents (backticks) to query pass for the Gmail password.

Note: I have two-factor authentication enabled for my Google accounts and so I needed to create a separate App Password for use with Mutt.

Growing Pains

There are a couple of things I realized quickly were less than ideal with my initial setup. In fact within a couple of minutes I had added the following setting:

set sort = reverse-date-received

For better or worse I’ve spent my whole life using email clients where the newest email (received or sent) was at the top of the list. Mutt’s default of it being on the bottom is completely anathema to me. So I changed it almost immediately.

My primary Gmail account is linked with multiple other Gmail accounts. So I needed a way to choose which account I was sending mail as:

set edit_headers = yes

Unholy Emails

Unfortunately in the world we live in, people love to send html emails. Some will by courteous enough to include a plain text version but many wont. The good news is that there are a decent number of command-line browsers that can convert html to plain text within Mutt itself.

First you have to define which browser to use in the mailcap file. By default Mutt looks in $HOME/.mailcap for this file but I prefer keeping everything in the $HOME/.mutt directory so I changed the default mailcap path.

set mailcap_path = "~/.mutt/mailcap"

For my browser I settled on elinks but most console browsers should work just fine.

text/html; elinks -dump; copiousoutput

The copiousoutput tells Mutt to we’re potentially passing large amounts of text and so Mutt invokes a pager. By default this is Mutt’s internal pager but you can define an external one using the pager setting in your muttrc.

Now I tell Mutt how to actually handle these files by putting the following in my muttrc.

alternative_order text/plain text/html
auto_view text/html

alternative_order prioritizes the plain text version of an email (if it exists) over it’s html variant. If it doesn’t exist, it’ll display the html version. However by default Mutt doesn’t automatically display any non plain text email. You have to invoke that manually, or just add the auto_view setting.

That’s all well and good but what about all the fancy html and styling that elinks just can’t render? There are going to be images and fancy formatting that I might want to view. In that case I have to resort to good old Firefox and put the following in my mailcap.

text/html; firefox %s;

It’s important to put that entry above all other text/html entires. That way Mutt will default to Firefox when manually evoked. Now I can easily open up an email in a Firefox tab by pressing v to view the attachments and m on the appropriate text/html attachment I want to view in Firefox.

Persistent Mutt

Initially I would launch Mutt, let it retrieve any new emails and then close Mutt when I was done with it. As time went on I found I just kept Mutt open in a terminal constantly. Unfortunately Mutt doesn’t automatically retireve emails by default. You have to manually sync with the server using $. This is easily remedied by adding a simple setting to my muttrc.

set imap_check_subscribed

By default, Gmail will have a list of folders. These are all defined in the Labels tab of Gmail’s settings. There’s a handful of default ones (some we’ve already seen like Trash, Sent, and INBOX) and any custom ones we can define. The imap_check_subscribed setting adds all these to the mailboxes list which are then automatically polled for new mail. I want to query everything for new mail but if I wanted to query only specific folders I could get rid of the imap_check_subscribed setting and add the individual folders to the mailboxes list like this:

mailboxes +INBOX +friends +work

There is a setting called mailcheck that determines how often Mutt checks the mailboxes for new mail. The default is 5 seconds which in my opinion is unnecessary for most people. I changed it to 120 seconds (or 2 minutes) for my needs.

There are a few other settings that worthwhile to look into. timeout, imap_keepalive, and imap_passive.

Good to Go

At this point we have a configuration that I would consider acceptable for everyday use. Here’s what mine looked like exactly.

## IMAP
set folder = "imaps://imap.gmail.com"

set imap_user = "bassam.saeed@gmail.com"
set imap_pass = `pass mail/gmail/mutt`

set   imap_check_subscribed
unset imap_passive
set   mail_check = 120

## SMTP
set smtp_url  = "smtps://bassam.saeed@gmail.com@smtp.gmail.com"
set smtp_pass = `pass mail/gmail/mutt`

set realname  = "Bassam Saeed"
set from      = "bassam.saeed@gmail.com"

set edit_headers = yes

## LOCATIONS
set header_cache     = "~/.mutt/cache/headers"
set message_cachedir = "~/.mutt/cache/bodies"
set mailcap_path     = "~/.mutt/mailcap"

## FOLDERS
set spoolfile = "+INBOX"
set postponed = "+[Gmail]/Drafts"
set trash     = "+[Gmail]/Trash"

## UI
alternative_order text/plain text/html
auto_view text/html

set sort = reverse-date-received

I am currently working on part two, hopefully it’ll be up soon.