Email, hate it or love it, we all have to deal with it. Thankfully, thanks to Emacs & mu4e you can have a productive and, dare I say, fun time working with emails.

About

In this guide I will guide you through setting up mu4e with multiple accounts, using isync & mu, as well as the basics of how to incorporate mu4e into your workflow, plus tips & tricks like using mu4e actions to apply git patches.

To follow this guide, make sure to enable IMAP/SMTP access for your email account. The steps to enable this may vary depending on your specific email provider, so I won’t cover that in this guide.

Why mu4e?

There are multiple options available in GNU Emacs for managing emails, such as rmail, gnus, notmuch, and mu4e.

Each of these options has its own complexities, depending on your specific use case.

In my personal experience, I find mu4e to be preferable due to its ease of setup, particularly when dealing with multiple accounts.

Installing MU & isync

To get started, install mu & isync

  • Arch Linux
$ sudo pacman -S mu isync
  • Gentoo
$ sudo emerge -av mu isync

NOTE: Make sure you to use emacs USEFLAG for mu

If mu is not packaged for your distro, I recommend changing to a new one. But if you insist, you might have success installing mu from source, using straight.el:

(straight-use-package 'mu4e)

Setting up mbsync

Edit your configuration: ~/.mbsyncrc

Example setup for Fastmail:

IMAPAccount fastmail
Host imap.fastmail.com
Port 993
User myaccount@fastmail.com
PassCmd "emacsclient -e '(password-store-get-field \"fastmail.com/myaccount@fastmail.com\" \"smtp\)'"
SSLType IMAPS
SSLVersions TLSv1.2
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore fastmail-remote
Account fastmail

MaildirStore fastmail-local
Path ~/Mail/Fastmail/
Inbox ~/Mail/Fastmail/Inbox
Trash ~/Mail/Fastmail/Trash/
SubFolders Verbatim

Channel fastmail
Far :fastmail-remote:
Near :fastmail-local:
Patterns *
Expunge None
CopyArrivalDate yes
Sync All
Create Both
SyncState *

Example setup for Gmail:

IMAPAccount gmail
Host imap.gmail.com
Port 993
User myaccount@gmail.com
PassCmd "pass google/mygmail.com"
SSLType IMAPS
SSLVersions TLSv1.2
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore gmail-remote
Account gmail

MaildirStore gmail-local
Path ~/Mail/Account-GMAIL/
SubFolders Verbatim

Channel gmail
Far :gmail-remote:
Near :gmail-local:
Patterns * ![Gmail]* "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail" "[Gmail]/Trash"
Expunge None
CopyArrivalDate yes
Sync All
Create Both
SyncState *
Settings you should adjust:
  • PassCmd this should output your account password. I personally use pass as my password manager.

In the fastmail example above I get my password with this emacs function:

(password-store-get-field \"fastmail.com/myaccount@fastmail.com\" \"smtp\)

password-store is a pass front-end for Emacs.

NOTE: You can instead of PassCmd, use Pass that just has your password in plain text, but I do not recommend this for security reasons.

  • CertificateFile - This location can vary based on your Linux distribution.

  • Patterns - Defines which folders will be synced, I personally just define my folders manually, similarly to the fastmail example.

Feel free to define multiple accounts this way.

After configuring isync, start the initial sync by running this command:

$ mbsync -a

Setting up mu & mu4e

First, you have to run the initial index.

mu init --maildir=~/Mail --my-address myaccount@fastmail.com
mu index 

NOTE: You may add as many --my-address arguments you want.

Now, it’s time to create your mu4e configuration for Emacs.

You may create this as a module, or just as part of your :config using use-package. I personally define variables in my init.el like this:

(defvar is-desktop (equal (system-name) "desktop-hostname")) ;; your $HOSTNAME
(defvar is-laptop (equal (system-name) "laptop-hostname"))

Create ~/.emacs.d/modules/thanos-mu4e.el for the mu4e module configuration. And add this to my init.el

(add-to-list 'load-path "~/.emacs.d/modules")

(when (or is-desktop is-laptop)
  (require 'thanos-mu4e))

This way I only load my mu4e configuration only when I’m using my laptop or desktop.

Loading Mu4e

Make sure mu4e is on load-path:

(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e/") ;; path to mu4e
(require 'mu4e)

The essentials

This is an example configuration to get started with mu4e using multiple accounts:

(setf mu4e-get-mail-command "mbsync -a")

(when (and is-desktop (server-running-p))
  (setf mu4e-update-interval (* 10 60)))

;; Snippet from EmacsWiki
(defun set-mu4e-context (context-name full-name mail-address signature)
  "Return a mu4e context named CONTEXT-NAME with :match-func matching
    folder name CONTEXT-NAME in Maildir. The context's `user-mail-address',
    `user-full-name' and `mu4e-compose-signature'`smtpmail-smpt-server' is set to MAIL-ADDRESS
    FULL-NAME SIGNATURE and SERVER respectively.
    Special folders are set to context specific folders."
  (let ((dir-name (concat "/" context-name)))
    (make-mu4e-context
     :name context-name
     ;; we match based on the maildir of the message
     :match-func
     `(lambda (msg)
	(when msg
	  (string-match-p
	   ,(concat "^" dir-name)
	   (mu4e-message-field msg :maildir))))
     :vars
     `((user-mail-address    . ,mail-address)
       (user-full-name       . ,full-name)
       (mu4e-sent-folder     . ,(concat dir-name "/Sent"))
       (mu4e-drafts-folder   . ,(concat dir-name "/Drafts"))
       (mu4e-trash-folder    . ,(concat dir-name "/Trash"))
       (mu4e-trash-folder    . ,(concat dir-name "/Starred"))
       (mu4e-refile-folder   . ,(concat dir-name "/Archive"))
       (mu4e-compose-signature . ,signature)))))

;;Fixing duplicate UID errors when using mbsync and mu4e
(setf mu4e-change-filenames-when-moving t)

(setf mu4e-maildir-shortcuts
      '(("/Fastmail/Inbox" . ?f)
	("/Drafts" . ?d)
	("/Sent" . ?s)
	("/Gmail-Account/[Gmail]/All Mail" . ?g)

(setf mu4e-contexts
      (list
       (make-mu4e-context
	:name "fastmail"
	:match-func
	(lambda (msg)
	  (when msg
	    (string-prefix-p "/Fastmail" (mu4e-message-field msg :maildir))))
	:vars '((user-mail-address . "username@fastmail.com")
		(user-full-name    . "User Name")
		(smtpmail-smtp-server . "smtp.fastmail.com")
		(smtpmail-smtp-service . 465)
		(smtpmail-stream-type . ssl)
		(mu4e-drafts-folder  . "/Drafts")
		(mu4e-sent-folder  . "/Sent")
		(mu4e-refile-folder  . "/Archive")
		(mu4e-trash-folder  . "/Trash")))
		;; You can comment out the second account.
       (make-mu4e-context
	:name "gmail"
	:match-func
	(lambda (msg)
	  (when msg
	    (string-prefix-p "/Gmail-Account" (mu4e-message-field msg :maildir))))
	:vars '((user-mail-address . "username@gmail.com")
		(user-full-name    . "User Name")
		(smtpmail-smtp-server . "smtp.gmail.com")
		(smtpmail-smtp-service . 465)
		(smtpmail-stream-type . ssl)))))
		
(setf message-send-mail-function 'smtpmail-send-it
      mu4e-compose-signature "My Signature"
      mu4e-compose-context-policy 'ask)

Extras

Mu4e actions

Mu4e actions makes it really simple to work with git patches, this is an example configuration you may like to use:

(setf mu4e-view-actions
      (delete-dups
       (append
	'(("gapply git patches" . mu4e-action-git-apply-patch)
	  ("mgit am patch" . mu4e-action-git-apply-mbox)
	  ("bb4 am patch" . mu4e-action-git-apply-b4)
	  ("ssetup reword list with b4" . mu4e-action-setup-reword-b4)
	  ("crun checkpatch script" . my-mu4e-action-run-check-patch)
	  ("MCheck if merged" . my-mu4e-action-check-if-merged)))))

Sign your emails using pgp

If you have setup your own pgp key, you may sign automatically all your emails using this hook:

(add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime)

Take a look at the functions defined for mml-mode-map for more.