How I Configured Doom Emacs as an Email Client

I recently spent some time configuring Doom Emacs to be my email client. It took a lot of trial and error, but I finally have a working configuration. Why did I do this? Because the Emacs bug has bitten me, and the fever has taken over. Let’s go over my configuration and talk through some pain points I encountered.

Emacs for the Uninitiated

Excuse the hyperbole, but Emacs can do everything. At its core is a Lisp interpreter that one can script to do most computing tasks. And, over its 40+ year lifespan, Emacs has grown. It’s now configurable to integrate with the most popular development tools and environments. I’ve been using it for many things these days, from project management to switching my music to the next track. I’ve even used it to control my Twitch stream. Yes, it’s that capable.

It appeals to me not because it can do everything. Rather, I’m impressed with the consistent approach across all these applications. The ability to configure workflows across all my computing use cases has been game-changing.

Batteries Included

Out of the box, Emacs is very capable, but both learning and configuring are quite a time investment. I started my journey with a pre-configured framework, for lack of a better word, called Doom Emacs. Doom provides a curated experience with pre-configured packages. giving newcomers and the time-restrained a head start.

Even with the running head start, I found setting up email to be less than easy. The Doom documentation, while great in some respects, is lacking in this area. A pain point in my acclimation to the Emacs environment: it’s quite fragmented. Quite a bit of Googling showed me learning resources close to my specific needs. But there was nothing 100% in the hand-holding territory.

The Steps

Enough preamble. Let’s walk through the setup and configuration of mbsync, mu, and mu4e with multiple Gmail SMTP/IMAP accounts. It seemed to me that Gmail presented more challenges than configuring other providers. Gmail presents SMTP in a different way than most. Conventionally, one would sync folders between one’s provider and a local email store. Gmail has “labels” disguised as SMTP folders.


First, let’s grab our dependencies, of which there are a few. This post assumes you’re running some flavor of Linux or MacOS and know how to use its package manager. (It also assumes an installation of Doom Emacs itself, which is outside the scope of this particular post.)

Grab yourself a copy of mbsync and mu. These are for syncing our mailboxes and their content. Here’s some convenient hand-holding.

# For Debian / Ubuntu flavors

sudo apt install mbsync

sudo apt install mu

#For Arch / Manjaro flavors

sudo pacman -S mbsync

sudo pacman -S mu

#For MacOS users

brew install isync

brew install mu


Now it’s time to configure mbsync. Create a file called .mbsyncrc and place it in your $HOME directory. I’ll include the content of my .mbsyncrc below. Make sure to replace every instance of the email address/account nicknames with your own.


Create Both
Expunge Both
SyncState *

IMAPAccount youraddress@gmail
User [email protected]
# You can also provide a plain text passwork like so:
# Password yourpasswordwithoutquotes
PassCmd "gpg --quiet --for-your-eyes-only --no-tty --decrypt \~/Mail\/[email protected]"
AuthMechs LOGIN
CertificateFile ~/.ssh/mu4e/cert.pem

IMAPStore account1@gmail-remote
Account youraddress@gmail

MaildirStore youraddress@gmail-local                  
Path ~/Mail/youraddress@gmail/
Inbox ~/Mail/youraddress@gmail/INBOX

Channel youraddress@gmail-inbox
Far :youraddress@gmail-remote:
Near :youraddress@gmail-local:
Patterns INBOX

Channel youraddress@gmail-sent
Far :youraddress@gmail-remote:"[Gmail]/Sent Mail"
Near  :youraddress@gmail-local:"[Gmail].Sent Mail"

Channel youraddress@gmail-trash
Far :youraddress@gmail-remote:"[Gmail]/Trash"
Near  :youraddress@gmail-local:"[Gmail].Trash"

Channel youraddress@gmail-archive
Far :youraddress@gmail-remote:"[Gmail]/All Mail"
Near  :youraddress@gmail-local:"[Gmail].All Mail"

Channel youraddress@gmail-drafts
Far :youraddress@gmail-remote:"[Gmail]/Drafts"
Near :youraddress@gmail-local:"[Gmail].Drafts"

Group youraddress@gmail
Channel youraddress@gmail-inbox
Channel youraddress@gmail-trash
Channel youraddress@gmail-archive
Channel youraddress@gmail-sent
Channel youraddress@gmail-drafts


For brevity, I’ve only included one account above. However, the provided config should work for as many accounts as you need so long as you give them unique names. Be sure to replace the content with your own addresses, etc.


Next, you’ll need to create a directory for your mailboxes to live.

mkdir -p ~/Mail/youraddress@gmail

If you’ve had your account for 10+ years like I have, this may take a while. Keep in mind that this will pull in every email you ever sent or received. Mine was 5 gigs. If this is an issue, you may consider omitting the “Sent Mail” and “All Mail” folders.

Within Gmail, be sure to enable IMAP under the setting page’s Forwarding and IMAP/SMTP tab.

Now it’s time to fetch our mailboxes. Run the following command:

mbsync --all

Now go make some coffee, since this might take several minutes. Once your emails have finished downloading, run the following command:

mu init --maildir ~/Mail --my-address [email protected] --my-address [email protected] # for each email address youre adding.

Now tell mu to index your mailboxes with:

mu index  

Now that we have the groundwork finished, we can finally start to configure mu4e, our Emacs email client.

(require 'mu4e)

;; list of your email adresses:
(setq mu4e-personal-addresses '("[email protected]"
                                "[email protected]"))

(setq mu4e-contexts
          :name "Gmail" ;; Give it a unique name. I recommend they start with a different letter than the second one.
          :enter-func (lambda () (mu4e-message "Entering gmail context"))
          :leave-func (lambda () (mu4e-message "Leaving gmail context"))
          :match-func (lambda (msg)
                        (when msg
                          (string= (mu4e-message-field msg :maildir) "/address1@gmail")))
          :vars '((user-mail-address . "[email protected]")
                  (user-full-name . "Your Name Here")
                  (mu4e-drafts-folder . "/address1@gmail/[Gmail].Drafts")
                  (mu4e-refile-folder . "/address1@gmail/[Gmail].All Mail")
                  (mu4e-sent-folder . "/address1@gmail/[Gmail].Sent Mail")
                  (mu4e-trash-folder . "/address1@gmail/[Gmail].Trash")
                  ;; SMTP configuration
                  (starttls-use-gnutls . t)
                  (smtpmail-starttls-credentials . '(("" 587 nil nil)))
                  (smtpmail-smtp-user . "[email protected]")
                  (smtpmail-auth-credentials .
                                             '(("" 587 "[email protected]" nil)))
                  (smtpmail-default-smtp-server . "")
                  (smtpmail-smtp-server . "")
                  (smtpmail-smtp-service . 587)))
          :name "Business Address" ;; Or any other name you like.
          :enter-func (lambda () (mu4e-message "Entering cablecar context"))
          :leave-func (lambda () (mu4e-message "Leaving cablecar context"))

          :match-func (lambda (msg)
                        (when msg
                          (string= (mu4e-message-field msg :maildir) "/address2@gmail")))
          :vars '((user-mail-address . "[email protected]")
                  (user-full-name . "Your Name Here")
                  (mu4e-drafts-folder . "/address2@gmail/[Gmail].Drafts")
                  (mu4e-refile-folder . "/address2@gmail/[Gmail].All Mail")
                  (mu4e-sent-folder . "/address2@gmail/[Gmail].Sent Mail")
                  (mu4e-trash-folder . "/address2@gmail/[Gmail].Trash")
                  ;; SMTP configuration
                  (starttls-use-gnutls . t)
                  (smtpmail-starttls-credentials . '(("" 587 nil nil)))
                  (smtpmail-smtp-user . "[email protected]")
                  (smtpmail-auth-credentials .
                                             '(("" 587 "[email protected]" nil)))
                  (smtpmail-default-smtp-server . "")
                  (smtpmail-smtp-server . "")
                  (smtpmail-smtp-service . 587)))

(setq mu4e-maildir-shortcuts  '((:maildir "/address2@gmail/INBOX"               :key ?i)
                                (:maildir "/address2@gmail/[Gmail].Sent Mail"   :key ?s)
                                (:maildir "/address2@gmail/[Gmail].Drafts"      :key ?d)
                                (:maildir "/address2@gmail/[Gmail].Trash"       :key ?t)
                                (:maildir "/address2@gmail/[Gmail].All Mail"    :key ?a)
                                (:maildir "/address1@gmail/INBOX"               :key ?I)
                                (:maildir "/address1@gmail/[Gmail].Sent Mail"   :key ?S)
                                (:maildir "/address1@gmail/[Gmail].Drafts"      :key ?D)
                                (:maildir "/address1@gmail/[Gmail].Trash"       :key ?T)
                                (:maildir "/address1@gmail/[Gmail].All Mail"    :key ?A)))

And with that, you can try to fetch your email with mu4e. In Doom, that’s a keystroke of SPC o m b u , which will land you in the “Today” box and fetch any fresh messages.

Note that above, when we configure our contexts, we give them unique names. In Doom Emacs, you can switch contexts with the ; key and then enter the first letter of the name of the context.

Configuring Doom Emacs as an Email Client: Wrapping Up

I hope this post will save you a good bit of the time and frustration I experienced. My biggest piece of advice would be to follow the mailbox naming conventions laid out here. Naming them something else will cause the creation of those Mailbox names inside Gmail, duplicating their contents. It took me several tries to get that right, wasting a lot of time and bandwidth in the meantime.


Join the conversation

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