NeoMutt: using IMAP and SMTP

Most people use an email client on their local machine and read email over IMAP. Well you can teach an old dog new tricks, NeoMutt has built-in support for IMAP! In this post we'll configure IMAP and dig into the different ways we can send outbound email, either using msmtp or connecting directly to our email providers SMTP server.

This is the second post in a series about NeoMutt. In the first one we configured NeoMutt for doing email like it's 1995 ... in 2025: jokes aside, that post configures all the foundation settings and explores key concepts like mailbox shortcuts.

To really follow along with this post you'll need an email provider. For many people that's going to be free email from one of the big providers like Google. Email is important to me because it's my online identity. And overall the centralisation of email to a small number of giant advert-based providers has been a bad thing. If you can afford it, I'd highly recommend using a specialist provider and your own domain.

I use Runbox a specialist email provider, with a particular focus on reliability, environmental sustainability and privacy. Their email also works on mobile and they have a Web interface. They are a small, specialist team who have a strong track-record - providing email for 25 years! The other well-known specialist who has a strong track record is Fastmail. Check them out!

Alright, let's remind ourselves of the current settings and then start configuring IMAP.

Current settings

At the end of the first post the basic settings looked like this:

# vim: ft=muttrc fdm=marker foldcolumn=2

set tmpdir           = ~/.config/neomutt/tmp

set from  = <username>@<domain>
set real_name = "My Name"

set use_threads = yes       # show email in threads in the Index
set pager_index_lines = 6   # show this many lines of the Index when
                            # reading email
set pager_context = 3       # lines of context between pages
set pager_stop = yes        # don't scroll of the end of one message onto
                            # the next

unset markers               # don't show + for wrapped lines in the pager
set mark_old = no           # don't show 'new' emails as old, if user has

set edit_headers = yes    # allow user to edit message headers
set editor       = "vim"  # define which editor to use

set fast_reply   = yes    # skip initial prompts when replying
set include      = yes    # include the message we're replying to

set sidebar_visible       = yes
set sidebar_width         = 15   # width in screen columns
set sidebar_next_new_wrap = yes  # wrap around the list when scrolling

set mail_check_stats      = yes  # check for new email and show stats

Caching headers and bodies

Going out to the network to download each email as the user scrolls down the Index view can be slow. Consequently, the first thing to do is to add caching to the top of ~/.config/neomutt/neomuttrc:

# vim: ft=muttrc fdm=marker foldcolumn=2

set header_cache     = ~/.cache/neomutt/cache/  # where to store headers
set message_cachedir = ~/.cache/neomutt/cache/  # where to store bodies

set header_cache_compress_method = "zstd"
set header_cache_compress_level = 3

set message_cache_clean = yes                   # not recommended to set this!

set tmpdir           = /tmp

The header_cache variable accepts a path which can be either a directory or a file. The benefit of setting a directory is that it creates a cache file per mailbox which may be faster if there are lots of mailboxes. Similarly, the message_cachedir creates a cache for message bodies and also accepts a path.

To compress the cache NeoMutt has to be compiled with lz4, zstd or zlib. To check this run neomutt -v and check the compression line:

compression: zstd

Assuming that compression is available the header_cache_compression_method accepts a string specifying which one to use. And header_cache_compress_level accept a number.

The NeoMutt Guide has a section on Local Caching. It advises against setting message_cache_clean because it can be slow for large folders. Without this setting the cache will grow if other email clients delete emails from the IMAP server. I set it and haven't had any problems yet.

If NeoMutt's being used on a single user system (e.g. a laptop) then tmpdir can also be changed to something suitable.

Configuring IMAP

Lots of configurations show setting $imap_user and some other settings that come from Mutt. NeoMutt has a more flexible approach which extends the URL syntax to embed the protocol, username and host into the URL:

proto://username:password@server:port/path

imaps://blah@myemail.domain@mail.runbox.com

In this example blah@myemail.domain is the username, and the IMAP server is mail.runbox.com.

The password for the IMAP server is set in imap_pass as a string. For testing this can just be the password in plain text. For a more secure approach I encrypt the IMAP password using GnuPG and configure it like this:

setenv PINENTRY_USER_DATA curses  # ask for the password in the terminal
set imap_pass="`gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/sec.gpg`"

One issue with using GnuPG encrypted files like this is that if the file isn't decrypted then NeoMutt will freeze. By default, NeoMutt is set to use pgp_use_gpg_agent and when it tries to decrypt it will use pinentry-tty which doesn't work very well. Consequently we specify that it should use pinentry-curses.

To tell NeoMutt where new email will appear set the spoolfile option to the URL of the IMAP server:

set spool_file   = imaps://user@email.domain@mail.runbox.com

We also set the folder so that we can use mailbox shortcuts to specify the other mailboxes. Since we're using the native IMAP capability this URL is for the IMAP server:

set folder      = imaps://user@email.domain@mail.runbox.com
set record      = +Sent
set trash       = +Trash
set postponed   = +Drafts

As we discussed in the first post the equal or plus sign are mailbox shortcuts and will expand to the location specified in the $folder variable. Consequently, $record will expand to imaps://user@email.domaint@mail.runbox.com/Sent.

The next part of the configuration is to set how often new email should be checked for:

set mail_check = 120                 # time in seconds between mail checks
set timeout    = 30                  # time to wait for user input
set mail_check_stats = yes
set mail_check_stats_interval = 120  # calculate every 120 secs

set imap_idle = yes

The mail_check variable accepts a number in seconds which determines how often NeoMutt will check for new email. The default is every 5 seconds, here we're setting it 120 seconds to avoid constant network traffic. NeoMutt is a single threaded application, so it's possible to block the application which will prevent it from checking for new email: the timeout variable sets how long NeoMutt will remain blocked in a menu or dialog before it aborts, here it's set to 30 seconds.

Aside from checking for new email NeoMutt can also calculate how many emails are in the mailbox and other stats like how many of those are marked as new. The mail_check_stats variable is a boolean, it determines whether NeoMutt will calculate those stats. The mail_check_stats_interval variable sets how often those stats are calculated, it defaults to every 60 seconds, for this configuration it's been set to every 120 seconds.

Finally imap_idle determines if NeoMutt will try to use the IDLE extension to check for new email in the current mailbox. This works on most IMAP servers, but if the connection to the IMAP server is unstable try turning this off.

IMAP Mailboxes

There are two ways we can set-up IMAP mailboxes.

The first way is for the IMAP server to provide the mailbox list to NeoMutt after the user has subscribed to the mailbox on the server side. To see the list of mailboxes on the IMAP server use the y key to open the folder browser: if it only shows the INBOX use c to Chdir and use the base server URL, this will clear the filter and it will show all the top-level folders.

To subscribe to a mailbox use the s key and unsubscribe with the u key. Note that if the imap_list_subscribed variable is set then the folder browser will only show subscribed mailboxes. Rather than specifying mailboxes using the mailboxes command, set the variable imap_check_subscribed and NeoMutt will add the subscribed folders to the mailboxes list.

The second way is to specify the mailboxes, just as we did previously:

mailboxes -label Inbox -notify -poll ! \
          -label Drafts -nonotify -nopoll =Drafts \
          -label Archive -nonotify -nopoll =Archive \
          -label Sent -nonotify -nopoll =Sent

As before the first line for the Inbox is using a mailbox shortcut, an exclamation mark, which will expand to the location we've specified for the $spool_file variable. The second line is using the equals sign shortcut which expands to the location set in the $folder variable.

That's all that's needed for native IMAP support in NeoMutt. The NeoMutt Guide IMAP Support page has other optional variables that can be configured. If there are issues with the IMAP connection being unstable try turning off the IDLE support (imap_idle = no) and see the Guide for other options to tune the connection.

Gmail IMAP

There are a few things to know about using Gmail with IMAP, here's the basic configuration:

set folder      = imaps://myusername@gmail.com@imap.googlemail.com

set spool_file   = +INBOX
set trash       = +[Gmail]/Bin
set postponed   = +[Gmail]/Drafts

unset record

mailboxes -label G:Inbox -notify -poll ! \
          -label Drafts -nonotify -nopoll +[Gmail]/Drafts \
          -label Sent -nonotify -nopoll '+[Gmail]/Sent Mail' \
          -label Archive -nonotify -nopoll '+[Gmail]/All Mail'

The first thing is that you have to switch IMAP on: this is in the Gmail settings in Forwarding and POP/IMAP.

By default Gmail keeps a record of all sent email automatically, so don't set $record or there will be repetition. Alternatively, it can be switched off in the Gmail settings ("Auto-Expunge off - Wait for the client to update the server").

Additionally, all email that's send by Web Gmail is kept under the [Gmail] folder hierarchy: use the folder browser (default key y) to explore the folders if there's difficulty in finding the precise folder path to use.

To access IMAP authentication also has to be configured. The easiest way is a standard password (which Google calls app passwords) - it will warn you they're dangerous. The other option is to use Oauth2 which the NeoMutt documents explain (OAUTHBEARER and XOAUTH2 Support).

New mail alerts

NeoMutt will check for new email regularly and show a message that there's new mail. That's not very useful if I'm in another window or virtual desktop! NeoMutt has a new mail feature feature which executes a script when it detects new mail in a mailbox.

The new_mail_command will call the specified command. It uses the status format string capabilities to provide information. In this example I'm using the %D which is the mailbox description (the label) so I know which mailbox has received new mail:

set new_mail_command="notify-send --app-name=Neomutt --icon='~/.config/neomutt/NeoMutt.png' \
    --urgency=low --expire-time=3000 'New Email in %D' &"

The NeoMutt logo is available on Github.

Sending Mail

To send outbound email we need an SMTP capability. The default configuration is that NeoMutt talks directly to a local mail server (using the sendmail) variable, this speaks to the original design where everyone was on a Unix host.

To emulate this configuration I use msmtp which is a light-weight MDA (Mail Delivery Agent) that sends email via my mail providers smarthost. NeoMutt talks to msmtp as if it were a local mail server, msmtp stores any email if the machine's not online and when it goes online msmtp sends (forwards) all queued email out through my mail provider's server (Runbox).

The advantage of this system is that I can send email when I'm not online. A completely offline set-up requires the ability to read email as well, so inbound email (e.g. IMAP) also has to be stored locally, we'll look at that in a future post. Anyway, for sending email msmtp is a nice set-up, see my full msmtp tutorial for the details.

The other option is to send email by directly speaking to the mail providers SMTP server. Since NeoMutt was designed for talking to a server that's on the same UNIX host it won't cache outbound email so the user has to be online to send email. Just the same as for IMAP to specify an SMTP server we use the URL feature (see NeoMutt Guide's SMTP Support) which looks like this:

smtp_url = smtps://user@email.domain@mail.runbox.com
smtp_auth = "`gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/sec.gpg`"

The smtp_url accepts a string which is the smarthost definition, and smtp_auth is a string containing the password to use.

Mixing local mailboxes

Using IMAP in this way requires the user to be online, but we might want to write emails when offline, and save emails to local folders. For example, my email Archive is on my local laptop. Both the mailbox definition variables and the mailboxes command give us that flexibility. For example:

# new email continues to come from the IMAP server
set spool_file   = imaps://user@email.domain@mail.runbox.com

# default location is set to the local filesystem
set folder      = ~/mail

set record      = +Sent
set postponed   = +Drafts

set trash       = !Trash

mailboxes -label Inbox -notify -poll ! \
          -label Drafts -nonotify -nopoll =Drafts \
          -label Archive -nonotify -nopoll =Archive \
          -label Sent -nonotify -nopoll =Sent

Now we can write an email while offline and postpone it with the P key. When online to recall postponed emails use R and NeoMutt will show a list of the postponed emails to send. When you're offline NeoMutt will error if it's started:

Could not connect to mail.runbox.com (Network is unreachable)
Unable to open mailbox imaps://user@email.domain@mail.runbox.com/

The way around this is to start NeoMutt specifying a local mailbox to open using the -f switch:

neomutt -f ~/.mail/Sent

Then write email and postpone them as previously.

Of course, this is all a bit of hack, for a full local-first set-up we need to mirror the IMAP servers contents locally, we'll cover that in a later post.

Sorting sorting

When NeoMutt starts the default sorting of the Index view is that the first email is the oldest one. Use the star (*) or <End> key to move to the last entry, and equals (=) or <Home> key to move to the first entry. If you have a lot of email flipping the index around so that the most recent emails are first is better:

set sort = reverse-last-date-received
set sort_aux = reverse-last-date-received

The sort variable accepts a variety of values, in this case it's sorting by the date received on an email, the date-received part. As the $use_threads variable is set, it modifies the behaviour so the last part means it will sort using the date received of the last email in the thread: the effect is it brings threads that have new activity up the Index list. Finally the reverse part means that the newest emails are at the top of the list - note that if you want to use reverse and last then reverse must come first (e.g. reverse-last-<whatever>).

The sort_aux variable is used when two messages match in the primary sort, and as we've set $use_threads it controls how sorting happens within a thread. Again, I want to see newer emails higher up in the thread so it's set to reverse-last-date-received.

Everyone has their own preference for sorting and threading email, there's lots of options in NeoMutt: see the Use Threads Feature for a full treatment.

Native IMAP complete

That's it for this time! We've covered how to use NeoMutt's native IMAP and the specific quirks associated with using Gmail's IMAP. We also looked at sending email through SMTP and why msmtp is a good choice if sending email while offline is important.

Did this post cover everything you needed to know about native IMAP in NeoMutt? If you have questions, comments or thoughts please email me, details on the About page. I can also be reached on Mastodon, futurile@mastodon.social.

Next time we'll move onto running multiple accounts at the same time and sprucing up the look of NeoMutt a bit with colours!


Posted in Tech Sunday 18 May 2025
Tagged with tech ubuntu guix email neomutt