I’ve been using Mutt for quite some time now and I find it great due to many things such as:

  • Very lightweight - I’m able to use it on any machine without making the machine unresponsive like with Thunderbird or Evolution when they decide to pull all of the 20-30 thousand messages I have in my mailbox
  • TUI interface - the fact I can use it from the terminal has helped me develop my whole workflow around it. I can use it on any WM, DE, or hell, I even use it on servers sometimes.
  • Powerful built-in features - It supports all the things I need, IMAP, SMTP, I can tag messages, manipulate tagged messages in one batch and so on. Once you dig under the hood a bit it becomes very useful in many areas

So without further ado here are some of the things I’ve tweaked in Mutt throughout the years in order to make it more usable for me.

Here’s the breakdown:

# Account details
set from="Ivan Tomica <MYEMAIL>"
set realname="Ivan Tomica"
set imap_user=MYEMAIL
set imap_pass=`~/.mutt/pass.sh`

You’ll notice here that I don’t save my password inside of the mutt config, reason for that is that I don’t want to have it stored in plain text. Although I don’t share any of my computers with anyone this is just a good practice I’ve caught on. Instead, I am fetching password to mutt at startup with pass.sh script.

Below is the script which fetches my password:

#!/bin/bash
#
# Helper for fetching password from the correct backend
#

ME=$(whoami)
BACKEND="kwallet"
CONFIG="/home/$ME/.muttpass.conf"
ACC="MYEMAIL"

if [ -f "$CONFIG" ]; then
	source "$CONFIG"
fi

if [ "$BACKEND" == "kwallet" ]; then
	kwallet-query -f Passwords -r "$ACC" kdewallet
elif [ "$BACKEND" == "gnome-keyring" ]; then
	secret-tool lookup "$ACC" password
else
	exit 0
fi

Depending on which machine I am there are usually different backends where I store the password. On machines belonging to the KDE family I opt to use kwallet in order to minimize number of packages installed on the machine. On GNOME based ones I use libsecret-tools package which hooks up to GNOME Keyring. In both cases password is stored in encrypted vault which is unlocked automatically when I log in to the machine.

Prerequisite is that you store the password inside appropriate backend. As you can see per script I can also override some options with local .muttpass.conf file.

# Set up folders
set folder="imaps://INCOMINGMAILSERVER/"
set spoolfile="+INBOX"
set postponed="+Drafts"
set record="+Sent"
set copy=yes
set imap_check_subscribed="yes"
set imap_list_subscribed="yes"
set imap_keepalive=180
unset imap_passive

Here I specify some basic folder settings and give mutt info where to connect to.

# Set cache
set header_cache=~/.mutt/mutt_cache
set message_cachedir=~/.mutt/message_cache
# SSL certs
set certificate_file=~/.mutt/mutt_certificates

Here I set up location for files holding message caches. It wouldn’t be wise for mutt to fetch all email headers (needed for rendering list of messages) and each message over and over. Instead, view is cached as well as the every message you open.

Same thing goes for SSL certificate (certificate of the server you’re connecting to) so it doesn’t ask you each time you’re opening mutt. It also helps to detect eventual MITM attack.

# SMTP settings
set smtp_url="smtp://$imap_user@MYSMTPSERVER:587"
set ssl_force_tls=yes
set smtp_authenticators="login"
set smtp_pass=`~/.mutt/pass.sh`

Same as with incoming server here I specify details for logging in and fetch the password from secure store.

# Reading Customization
set timeout=10
set mail_check=5
set beep_new=yes
set sort=threads
# If not set then "->" indicates real threads (need to have proper
# "In-Reply-To:" or "References:" email headers) and "*>" indicates pseudo
# threads (by subject only). 
set strict_threads=yes
set sort_aux=reverse-last-date-received
set move=no
ignore *
unignore Subject: Date: From: To: CC: Bcc:
hdr_order Subject: Date: From: To: CC: Bcc:
set index_format="%5C |[%Z]| %{%F %R} | %-20.20L | %?l?%4l&%4c? | %s"

set mailcap_path=~/.mutt/mailcap
auto_view text/html

Although options are pretty self-explanatory here I do basically tweaking of the view stuff. First I set up beep for new messages and sort and group messages by threads. Messages are also sorted by the last date received (newest messages are on top).

From there on I  adjust headers I see while viewing the message as well as the fields in message list

Mailcap is the place where I define per Mime-Type rendering. Basically ensures I can view HTML messages as plain text as opposed to seeing all the HTML tag gibberish. It looks like:

text/html; lynx -assume_charset=%{charset} -display_charset=utf-8 -dump %s; nametemplate=%s.html; copiousoutput

In order to bind my multi-key shortcuts I then unbind few keys in their appropriate views

# Disable startup warnings
bind    index   g       noop
bind    index   C       noop
bind    pager   C       noop

So basically g in index view and C (capital c) in both index and pager view.

This allows me to do my standard key bindings and macros:

# Key bindings
bind    index   G  imap-fetch-mail

macro   index   gi "<change-folder>=Inbox<enter>"       "Go to Inbox"
macro   index   ga "<change-folder>=Archive<enter>"       "Go to Archive"
macro   index   gs "<change-folder>=Sent<enter>"    "Go to Sent"
macro   index   gd "<change-folder>=Drafts<enter>"  "Go to Drafts"
macro   index   gt "<change-folder>=Trash<enter>"   "Go to Trash"
macro   index   gj "<change-folder>=Junk<enter>"    "Go to Junk"

# Sidebar navigation
bind index CP sidebar-prev
bind index CN sidebar-next
bind index CO sidebar-open
bind pager CP sidebar-prev
bind pager CN sidebar-next
bind pager CO sidebar-open

# b toggles sidebar visibility
macro index b "<enter-command>toggle sidebar_visible<enter><refresh>"    "Toggle sidebar"
macro pager b "<enter-command>toggle sidebar_visible<enter><redraw-screen>"    "Toggle sidebar"

# Archive messages
# - archives all the tagged messages
# - if none of the messages are tagged, it archives currently selected one
macro index,pager y "<tag-prefix><save-message>=Archive<enter><enter><sync-mailbox><enter>" "Archive"

For composing email I like to use vim, but I also set up text wrapping and limit it to 80 character width. I may remove that at some point in future as almost every email client now-days renders email properly and wraps it at screen edge

# Composing Mail
set editor='vim -c "set textwidth=80" -c "set wrap"'
set markers=no
set indent_string=">"
set signature=~/.mutt/sig
set include=yes
set forward_format="Fwd: %s"

Additional settins include quoting messages with “>” character and sourcing my email signature from separate file

All messages I forward also get prepended with Fwd in subject field.

To have things less cluttered I only have few folders in my sidebar and they are as follows

# which mailboxes to list in the sidebar
mailboxes "=Inbox"
mailboxes "=Archive"
mailboxes "=Drafts"
mailboxes "=Trash"
mailboxes "=Junk"
mailboxes "=Sent"

PGP/GPG is also the thing I use, although, I sincerely don’t remember the last time I’ve used it for email communication. There are messages from some security announcement mailing lists which use it and I find it useful to have mutt highlight those messages for me.

# GPG settings
source ~/.mutt/gpg.rc
set pgp_use_gpg_agent = yes
set pgp_auto_decode = yes
set pgp_sign_as = MYSIGNINGKEYHASH
set pgp_timeout= 3600
set crypt_autosign = yes
set crypt_replyencrypt = yes
set crypt_verify_sig = yes

And last but not least, here are some things I use to speed up rendering and fetching of messages

# update progress every n messages read
set read_inc=1000
# update display every n miliseconds
set time_inc=500
# update diplay every n kilobytes for net ops
set net_inc=100
# update progress every n messages written
set write_inc=100