Configuring Emacs mu4e with nullmailer, offlineimap and multiple identities

mu4e is a mail user agent for your Emacs. After leaving GMail a few months ago, this is the first MUA that I am loving even more. The major reasons for this are the faster than GMail real-time search (e.g. press s, then from:buddy flag:attach design review and watch it search my 68 thousand email archive in a fraction of a second), its Emacs foundation (the more I use it, the better I get at customizing it) and the observation that having my email in an uncluttered, by default text-only interface, somehow helps me to maintain the feeling of control.

A screenshot of my mu4e, showing the results of the search query “flag:list debian”.

This post explains briefly how to setup nullmailer (for sending mail really quickly via your SMTP server), offlineimap (for keeping your local IMAP folders in sync with your IMAP server) and finally mu4e.


mu4e can send mail by connecting directly to your SMTP server, and there’s a commented-out section in the configuration below that shows you how to do this. However, this can sometimes be slow, so I opted to install nullmailer on my laptops. nullmailer is a sendmail lookalike with its own queue, so that mu4e can hand-off your outgoing mail and get back to you immediately whilst nullmailer takes care of the sending in the background.

To support encrypted SMTP (highly recommended), you need at least version 1.10 of nullmailer. On Ubuntu 12.04 I made use of this backport. On Ubuntu 14.04, the nullmailer is up to date and you can simply apt-get install nullmailer.

In /etc/nullmailer/remotes, readable only to its owner, I have the following: smtp --port=587 --starttls --user=myusername --pass=mypassword


I’m using offlineimap 6.5.5, built from source.

My ~/.offlineimaprc looks like this:

accounts = myaccountname
maxsyncaccounts = 3

[Account myaccountname]
localrepository = Local
remoterepository = Remote
# speeds up syncing!
status_backend = sqlite
# -1: always do quick updates
#  0: never do quick updates
quick = -1

[Repository Local]
type = Maildir
localfolders = ~/Maildir

[Repository Remote]
type = IMAP
remotehost =
remoteuser = myusername
remotepass = mypassword
ssl = yes
# new comodo fingerprint (SHA1)
cert_fingerprint = 8f1e5dcaf69941a8be9fd04c7f4633895f4d2ead
# use at least two threads to sync mail (speeds up)
maxconnections = 2
realdelete = yes

This is for a straight-forward dovecot IMAP server (I manage my own). Please use Google for GMail setups.


Install mu and mu4e from source. On Ubuntu, it goes like this:

sudo apt-get install libgmime-2.6-dev libxapian-dev
sudo apt-get install guile-2.0-dev html2text xdg-utils
git clone
cd mu
autoreconf -i && ./configure && make
sudo make install

Study the following mu4e configuration, then modify and add to your init.el (or similar).

This configuration (mine) supports multiple identities that can be activated by either pressing a shortcut key in the main screen or headers, or when you’re replying to an email that was sent to an email that’s associated with one of your identities. Each identity is configured by a function that you define. Here that includes the from address and the signature.

;; we'll use this to read your different signatures from files
(defun get-string-from-file (filePath)
  "Return FILEPATH's file content."
    (insert-file-contents filePath)

;; this is where the install procedure above puts your mu4e
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")

(require 'mu4e)

;; path to our Maildir directory
(setq mu4e-maildir "/home/cpbotha/Maildir")

;; should mu4e use fancy utf characters? NO. they're ugly.
;;(setq mu4e-use-fancy-chars 't)

;; give me ISO(ish) format date-time stamps in the header list
(setq  mu4e-headers-date-format "%Y-%m-%d %H:%M")

;; customize the reply-quote-string
;; M-x find-function RET message-citation-line-format for docs
(setq message-citation-line-format "%N @ %Y-%m-%d %H:%M %Z:\n")
(setq message-citation-line-function 'message-insert-formatted-citation-line)

;; show full addresses in view message (instead of just names)
;; toggle per name with M-RET
(setq mu4e-view-show-addresses 't)

;; without this, "symbol's value as variable is void: mml2014-use" when signing
;; then found
;; so set to epg and all was good!
;; to sign a mail: M-x mml-secure-sign-pgpmime
(setq mml2015-use 'epg)

;; the next are relative to `mu4e-maildir'
;; instead of strings, they can be functions too, see
;; their docstring or the chapter 'Dynamic folders'
(setq mu4e-sent-folder   "/Archives.2014"
      mu4e-drafts-folder "/Drafts"
      mu4e-trash-folder  "/Trash")

;; the maildirs you use frequently; access them with 'j' ('jump')
(setq   mu4e-maildir-shortcuts
    '(("/Archives.2014"     . ?a)
      ("/INBOX"       . ?i)
      ("/Inbox-WORK"        . ?w)
      ("/Archives.2014"        . ?s)))

;; list of my email addresses.
(setq mu4e-user-mail-address-list '(""

;; when you want to use some external command for html->text
;; conversion, e.g. the 'html2text' program
;; (cpbotha: html2text sees to work better than the built-in one)
(setq mu4e-html2text-command "html2text")

;; mu4e-action-view-in-browser is built into mu4e
;; by adding it to these lists of custom actions
;; it can be invoked by first pressing a, then selecting
(add-to-list 'mu4e-headers-actions
             '("in browser" . mu4e-action-view-in-browser) t)
(add-to-list 'mu4e-view-actions
             '("in browser" . mu4e-action-view-in-browser) t)

;; the headers to show in the headers list -- a pair of a field
;; and its width, with `nil' meaning 'unlimited'
;; (better only use that for the last field.
;; These are the defaults:
(setq mu4e-headers-fields
    '( (:date          .  25)
       (:flags         .   6)
       (:from          .  22)
       (:subject       .  nil)))

;; program to get mail; alternatives are 'fetchmail', 'getmail'
;; isync or your own shellscript. called when 'U' is pressed in
;; main view.

;; If you get your mail without an explicit command,
;; use "true" for the command (this is the default)
(setq mu4e-get-mail-command "offlineimap")

;; setup default identity here:
;; general emacs mail settings; used when composing e-mail
;; the non-mu4e-* stuff is inherited from emacs/message-mode
(setq user-mail-address ""
      user-full-name  "Charl P. Botha")

;; set this to nil so signature is not included by default
;; you can include in message with C-c C-w
(setq mu4e-compose-signature-auto-include 't)
(setq mu4e-compose-signature (with-temp-buffer
                               (insert-file-contents "~/.signature.personal")
;; message-signature-file NOT used by mu4e
(setq message-signature-file "~/.signature.personal")

;; many recipes online use an alist with the different email identities
;; I like to use these functions, because then I have more flexibility
(defun cpb-mu4e-personal()
  (message "personal mail account")
  (setq  user-mail-address ""
         mu4e-compose-signature (get-string-from-file "~/.signature.personal"))

(defun cpb-mu4e-vxlabs()
  (message "vxlabs mail account")
  (setq  user-mail-address ""
         mu4e-compose-signature (get-string-from-file "~/.signature.vxlabs"))

(defun cpb-mu4e-trep()
  (message "treparel mail account")
  (setq  user-mail-address ""
         mu4e-compose-signature (get-string-from-file "~/.signature.treparel"))

(defun cpb-mu4e-medvis()
  (message "medvis mail account")
  (setq  user-mail-address ""
         mu4e-compose-signature (get-string-from-file "~/.signature.medvis"))

;; quickly change account. got his idea from:
(define-key mu4e-main-mode-map (kbd "<f1>") 'cpb-mu4e-personal)
(define-key mu4e-main-mode-map (kbd "<f2>") 'cpb-mu4e-vxlabs)
(define-key mu4e-main-mode-map (kbd "<f4>") 'cpb-mu4e-trep)
(define-key mu4e-main-mode-map (kbd "<f6>") 'cpb-mu4e-medvis)
(define-key mu4e-headers-mode-map (kbd "<f1>") 'cpb-mu4e-personal)
(define-key mu4e-headers-mode-map (kbd "<f2>") 'cpb-mu4e-vxlabs)
(define-key mu4e-headers-mode-map (kbd "<f4>") 'cpb-mu4e-trep)
(define-key mu4e-headers-mode-map (kbd "<f6>") 'cpb-mu4e-medvis)

;; for sendmail read this
;; am using nullmailer, so my mail sending just became STUPID fast
(setq message-send-mail-function 'message-send-mail-with-sendmail)

;; smtp mail setting - if you DON'T want to use nullmailer, instead
;; connecting to your smtp server and waiting...
;; (setq
;;    message-send-mail-function 'smtpmail-send-it
;;    smtpmail-stream-type 'starttls
;;    smtpmail-default-smtp-server ""
;;    smtpmail-smtp-server ""
;;    smtpmail-smtp-service 587

;;    ;; if you need offline mode, set these -- and create the queue dir
;;    ;; with 'mu mkdir', i.e.. mu mkdir /home/user/Maildir/queue
;;    smtpmail-queue-mail  nil
;;    smtpmail-queue-dir  "/home/user/Maildir/queue/cur")

;; don't keep message buffers around
(setq message-kill-buffer-on-exit t)
;; attachments go here
(setq mu4e-attachment-dir  "~/Downloads")

;; when you reply to a message, use the identity that the mail was sent to
;; the cpbotha variation -- function that checks to, cc and bcc fields
(defun cpb-mu4e-is-message-to (msg rx)
  "Check if to, cc or bcc field in MSG has any address in RX."
  (or (mu4e-message-contact-field-matches msg :to rx)
      (mu4e-message-contact-field-matches msg :cc rx)
      (mu4e-message-contact-field-matches msg :bcc rx)))

;; we only do something if we recognize something (i.e. no stupid default)
(add-hook 'mu4e-compose-pre-hook
          (defun my-set-from-address ()
            "Set current identity based on to, cc, bcc of original."
            (let ((msg mu4e-compose-parent-message)) ;; msg is shorter...
              (if msg
                   ((cpb-mu4e-is-message-to msg (list ""
                   ((cpb-mu4e-is-message-to msg (list ""
                   ((cpb-mu4e-is-message-to msg (list ""
                   ((cpb-mu4e-is-message-to msg "")

;; convenience function for starting the whole mu4e in its own frame
;; posted by the author of mu4e on the mailing list
(defun mu4e-in-new-frame ()
  "Start mu4e in new frame."
  (select-frame (make-frame))

Getting started

After everything has been configured, download and index your email by doing:

# download all imap email, this can take while
# index all of the mail -- initial index takes a while, then fast!
mu index --maildir=~/Maildir

In the future, press U on the mu4e main screen, or C-S-u on the headers or view screen to retrieve and index new mail from within Emacs.


I hope that you find this howto and configuration useful. Please let me know in the comments if you got it working, or if you have any questions.

Enjoy your mail again with mu4e!

4 thoughts on “Configuring Emacs mu4e with nullmailer, offlineimap and multiple identities”

  1. Thanks for putting this up! I added one bit to your setup, which maybe you or others find useful.

    The email account of a draft changes when starting another draft with another account. This got me a few times as I often start one one draft but then only finish it later. By making the variables buffer local, this can be avoided:

    (defun cpb-make-buffer-local ()
    (make-local-variable 'mu4e-compose-signature)
    (make-local-variable 'user-mail-address)
    (add-hook 'mu4e-compose-mode-hook 'cpb-make-buffer-local)

  2. Hi,

    how do you set up with highlights for different quotes? I mean, paragraph with ‘>’ and ‘ > >’ have different colours.


  3. Dear Yi.

    To change the font rendering (“face” in Emacs language) you can tweak the face definition.
    In my .emacs I have this:

    ‘(message-cited-text ((t :inherit (putHereYourDefaultFace) :foreground “Gray10”)))
    ‘(mu4e-cited-1-face ((t :inherit (putHereYourDefaultFace) :foreground “Gray10”)))
    ‘(mu4e-cited-2-face ((t :inherit mu4e-cited-1-face :foreground “Gray20”)))
    ‘(mu4e-cited-3-face ((t :inherit mu4e-cited-2-face :foreground “Gray30”)))
    ‘(mu4e-cited-4-face ((t :inherit mu4e-cited-3-face :foreground “Gray40”)))
    ‘(mu4e-cited-5-face ((t :inherit mu4e-cited-4-face :foreground “Gray50”)))
    ‘(mu4e-cited-6-face ((t :inherit mu4e-cited-5-face :foreground “Gray60”)))
    ‘(mu4e-cited-7-face ((t :inherit mu4e-cited-6-face :foreground “Gray70”)))

    This gives you a text that the older, the clearer. 🙂


Leave a Reply

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