Using mutt instead of MS Outlook on an Exchange server

Problem Statement

  • MS Outlook & Exchange sucks but that’s what your company supports.
  • Mutt over IMAP is too slow. What you want is something like mutt + spool like the good email old days.
  • Just about every GUI-based email client sucks (any OS). They are too slow compared to mutt.

Design Goals

  • Use mutt from local disk to keep mutt fast.
  • Keep mail on corporate IMAP/Exchange servers so it is still backed up & maintained by the IT dept.
  • Use IMAP because Exchange servers support it.
  • No manual sync or push. Automate everything so all you have to do is use mutt.
  • Allow straight-forward use of GUI email clients should you ever need them.

Design Concepts

  • OfflineIMAP synchronizes mail folders between the Exchange server and your machine (mutt’s local disk).
  • You read mail in mutt. Mutt reads mail from disk.
  • You write mail filtering rules in fdm.  fdm filters mail into folders to your liking.
  • Use cron to automate syncs and filtering jobs behind the scenes.


Step 1: Install ubuntu packages: mutt, offlineimap, fdm, msmtp (ubuntu 10.04 LTS package names)

Step 2: Config mutt to send mail using msmtp


account default
port 587
auth login
user donn
password s00perSekrit 
tls on
tls_starttls on
tls_certcheck off
#tls_fingerprint FE:39:F9:B4:64:31:0E:DF:31:51:72:DA:A7:4F:35:4B
logfile ~/.msmtp.log

Paste view:


set sendmail=/usr/bin/msmtp

Step 3: Setup Maildir directory

Add to ~/.muttrc:

set mbox_type=Maildir
set folder="~/Maildir"
mailboxes `echo -n "+ "; for file in ~/Maildir/*; do box=$(basename "$file"); echo -n "\"+$box\" "; done`
set spoolfile="~/Maildir/work/INBOX"
set postponed="+Drafts"
set record="+work/Sent"

Paste view:

Step 4: Setup OfflineIMAP

The key point of offlineimap: Mutt can read your messages from local hard disk, instead of over the network. This greatly reduces the lag / delay when reading messages quickly.

OfflineIMAP syncs your ~/Maildir/work message base with MS Exchange over IMAP. So, if you read a message in mutt, it will be marked read in ~/Maildir, and then offlineimap will sync this change with Exchange. If you later check your inbox with Thunderbird, OWA, Outlook, or other client, that message will have ‘read’ status (awesome). OfflineIMAP downloads new mail from Exchange and stores them in ~/Maildir/work/INBOX, but this is really the syncing process: Exchange has the new messages, ~/Maildir/work/INBOX does not, until after a sync.


accounts = Work
[Account Work]
localrepository = Local
remoterepository = Remote
[Repository Local]
type = Maildir
localfolders = ~/Maildir/work
[Repository Remote]
type = IMAP
remotehost =
ssl = yes
remoteuser = donn
remotepass = myPass
# Folders to skip during sync.
folderfilter = lambda foldername: foldername not in ['Deleted Items', 'Contacts', 'Calendar', 'Trash', 'MyFolderOfReallyOldMail']

Paste view:

Step 5: Setup FDM

FDM is used to filter mail between your INBOX and your other Maildir “folders”. For example, I filter mailing-list email to a separate folder. OfflineIMAP puts ALL incoming mail into INBOX, then FDM moves messages from INBOX to other folders based on your rules. All of this FDM work is done on local Maildir subdirs (local disk). I also have FDM move (ie. archive) all email over 30 days to a local, “old mail” folder.

WARNING: Be extra cautious as you develop your FDM rules. If you make a typo (eg. regex typo), you could accidentally delete incoming mail. Use the -n switch (test syntax) and -v (verbose output) to check your rules carefully.

#unmatched keep default. A lot of these, just to be safe.
set unmatched-mail keep
# Delivery actions.
action "INBOX" maildir "%h/Maildir/work/INBOX"
action "widgets-list" maildir "%h/Maildir/work/widgets-list"
action "ix_email" maildir "%h/Maildir/work/ix_email"
action "inbox_overflow" maildir "%h/Maildir/work/inbox_overflow"
#Bread and Butter INBOX account only operates on one folder
account "WORK" maildir "%h/Maildir/work/INBOX"
# Match regex's are *not* case-sensitive by default.
match account "WORK" {
 match "^subject:.*\\[ubuntu-widgets\\].*" in headers {
 match all action "widgets-list"
 # Negative match regex.
 match "^(to:|cc:)(.*|.*tech-l@ams-ix.*)" in headers {
 match "^From:(?!.**)" in headers {
 # If not from linx admins, move it.
 match all action "ix_email"
 # Move older msgs to overflow box so fdm doesn't have to process them over and over.
 # My email is donn @
 match age > 1 months {
 match "^To:(?!.*donn@.*)" in headers {
 # If not to-donn, archive the old mail to overflow box.
 match all action "inbox_overflow"
 # Last catchall match term.
 match unmatched action keep
# Send all mail to inbox.
match unmatched action keep

Paste view:

Step 6: Setup mutt basics


set hostname=""
set realname="Donn Lee"
set from=""
set envelope_from=yes
set hidden_host=yes
set use_domain=yes
set pager_stop=yes
my_hdr From:
set attribution="%n <%a> wrote on %{%a} [%{%Y-%b-%d %H:%M:%S %Z}]:"
set strict_threads = no
set date_format="!%a, %b %d, %Y at %I:%M:%S%p %Z"
set index_format="%4C %Z[%[%a %m/%d %H:%M]] %-16.16L%?X? [%X]? (%?l?%4l&%4c?) %s"
set pager_index_lines=20
# Change default subject format for fwd'd msgs.
set forward_format="Fwd: %s"
# Use emacs to compose mail.
set editor = "/usr/bin/emacs -nw %s"
# Create a nice status bar.
set status_format=" %r %b %f New:%n Del:%d TotMsgs:%m Tag:%t Flag:%F Size:%l %> Lim:%V (%P)"
# Ignore all lines by default
ignore *
# Set what I want to see
unignore from to cc subject date reply-to mail-followup-to x-url organisation organization x-mailer user-agent xmail-sent-to
hdr_order from to cc subject date reply-to mail-followup-to x-url organisation organization x-mailer user-agent xmail-sent-to
# Create a cache for performance.
set header_cache="~/.mutt_cache"
set maildir_header_cache_verify="yes"
set header_cache_pagesize="65536"
# And all your other mutt goodies...

Paste view:

Step 7: Read messages that are html formatted

Add to muttrc:

# View html email
# Must also add these two lines to ~/.mailcap
# text/html; links %s; nametemplate=%s.html
# text/html; links -dump %s; nametemplate=%s.html; copiousoutput
auto_view text/html

Paste view:

Step 8: Opening email attachments

I use Outlook Web App (OWA) to open an attachment (eg. a Powerpoint file). I always have a browser up and OWA is usually loaded in one of the tabs.


2 thoughts on “Using mutt instead of MS Outlook on an Exchange server”

  1. I’ve been trying to use oimfineflap for a number of months. While I haven’t noticed any lost mail, it has been very unreliable. I just can’t run it from a cron job or continuously in a terminal and not have it jam up somewhere. Deadlocks and other issues seem quite common. Unfortunately I haven’t had the time to run it in debug mode and see just what is going on but it looks rather complicated and I’m not sure I will ever bother. Other people have reported it being very reliable so I have no idea what is going on. I am synchronizing four different email accounts with it (two dovecot imap, one exchange, one gmail) so maybe that is a source of problems. I’m planning on switching back to direct IMAP access.

    1. Try mbsync/isync. It’s worked very well for me, though occasionally it does shit itself.

      I’d also suggest trying a systemd user unit (i.e., a timer unit). There might be a timeout setting — I have no idea.

Leave a Reply

Your email address will not be published. Required fields are marked *