Andrew Mercer
on this page

NeoMutt is a fork of Mutt that started in 2016. For years, many popular Mutt features (sidebar, notmuch integration, compressed folders, nested conditional format strings, and more) existed only as out-of-tree patches that distributions applied inconsistently. NeoMutt collected them into one actively developed codebase. Some of those features were later merged back into Mutt itself (the sidebar is the best-known example), but NeoMutt has kept adding its own since.

Both projects are alive. Mutt is conservative and stable, with infrequent releases. NeoMutt moves faster and releases several times a year with date-based versions (neomutt -v prints something like NeoMutt 20250905).

Should I use Mutt or NeoMutt?

Choose NeoMutt if you want… Choose Mutt if you want…
notmuch full-text search and tag-based virtual folders The most conservative, slowest-changing option
Lua scripting, ifdef conditional config, named-mailboxes Behaviour that matches a decade of existing Mutt documentation exactly
Modern header-cache backends (LMDB, RocksDB) with compression A package available on almost every OS, including very old ones
IMAP QRESYNC/CONDSTORE for faster resyncs on large mailboxes
Secure defaults out of the box (e.g. ssl_force_tls on)

Everything in the Mutt and Mutt and GPG guides applies to NeoMutt. This guide covers only what's different or extra.

Installing

sudo dnf install neomutt     # Fedora / RHEL (EPEL)
sudo apt install neomutt     # Debian / Ubuntu
brew install neomutt         # macOS

Check what's compiled in. Optional features vary by distribution build:

neomutt -v | sed -n '/Compile options/,/^$/p'

Look for +notmuch, +lua, +gpgme, +sasl, and the header-cache backends (+lmdb, +tokyocabinet, +kyotocabinet, +rocksdb).

Configuration files and migrating from Mutt

NeoMutt looks for its own files first, then falls back to Mutt's:

~/.neomuttrc
$XDG_CONFIG_HOME/neomutt/neomuttrc      (usually ~/.config/neomutt/neomuttrc)
~/.neomutt/neomuttrc
…then the Mutt locations (~/.muttrc, ~/.mutt/muttrc, ~/.config/mutt/muttrc)

So an existing Mutt config usually just works. To migrate deliberately:

mkdir -p ~/.config/neomutt
cp -r ~/.config/mutt/* ~/.config/neomutt/
mv ~/.config/neomutt/muttrc ~/.config/neomutt/neomuttrc
# fix any paths inside that still point at ~/.config/mutt
grep -rl '\.config/mutt' ~/.config/neomutt | xargs sed -i 's#\.config/mutt#.config/neomutt#g'

# Start with only this config and read any warnings
neomutt -n -F ~/.config/neomutt/neomuttrc

NeoMutt reports unknown or removed variables at startup instead of silently ignoring them. What usually needs changing:

  • Deprecated synonyms that Mutt still tolerates (for example pgp_autosign, pgp_replysign, pgp_replyencrypt) should be updated to their crypt_* names.
  • Patch-era variable names from distro-patched Mutt builds (old sidebar patch names, nm_* from early notmuch patches) may have been renamed. Check the startup messages.
  • Debug flags: Mutt's -d LEVEL writes ~/.muttdebug0; NeoMutt takes -d LEVEL -l FILE.

Sharing one config between Mutt and NeoMutt

NeoMutt's ifdef/ifndef let one file serve both clients, or degrade gracefully when an optional feature isn't compiled in. Mutt doesn't understand these commands, so keep NeoMutt-only blocks in a separate file that only NeoMutt sources:

# neomuttrc
source ~/.config/mutt/muttrc            # the shared, Mutt-compatible config
source ~/.config/neomutt/extras.rc      # NeoMutt-only features
# extras.rc: only apply settings if the feature exists in this build
ifdef sidebar_visible 'set sidebar_visible = yes'
ifdef nm_query_type   'source ~/.config/neomutt/notmuch.rc'
ifndef lua-source     'echo "built without Lua"'

finish (stop reading the current file) is also available, for guard clauses like ifndef nm_query_type finish.

set sidebar_visible       = yes
set sidebar_width         = 30
set sidebar_short_path    = yes
set sidebar_folder_indent = yes
set sidebar_format        = "%D%?F? [%F]?%* %?N?%N/?%S"
set sidebar_non_empty_mailbox_only = no
set mail_check_stats      = yes      # needed for the counts in sidebar_format

# Friendly names instead of raw IMAP paths (NeoMutt only)
named-mailboxes "Inbox"    "+INBOX"
named-mailboxes "Work"     "+Folders/Work"
named-mailboxes "Archive"  "+Archive"

bind index,pager \CP sidebar-prev
bind index,pager \CN sidebar-next
bind index,pager \CO sidebar-open
bind index,pager B   sidebar-toggle-visible

In sidebar_format: %D is the descriptive name, %N new count, %S total, %F flagged, %* padding to the right edge, and %?X?…? shows … only when X is non-zero.

notmuch: search and tag-based folders

notmuch indexes a local Maildir (typically kept in sync by mbsync, see Mutt → Offline mail) and makes full-text search across years of mail instant. NeoMutt can browse notmuch queries as if they were folders.

sudo dnf install notmuch       # or apt install notmuch
notmuch setup                  # point it at ~/Mail, set your name and addresses
mbsync -a && notmuch new       # sync then index; run both from a timer
# notmuch.rc
# Point NeoMutt at the database. Depending on your NeoMutt version this is
# $nm_default_url or a notmuch:// $folder. `neomutt -D | grep '^nm_'` shows what your build has.
set nm_default_url = "notmuch:///home/you/Mail"

# Virtual mailboxes are saved notmuch queries
virtual-mailboxes \
  "Inbox"      "notmuch:///home/you/Mail?query=tag:inbox" \
  "Unread"     "notmuch:///home/you/Mail?query=tag:unread" \
  "Flagged"    "notmuch:///home/you/Mail?query=tag:flagged" \
  "Last 7d"    "notmuch:///home/you/Mail?query=date:7d..&limit=500"

set nm_query_type    = threads     # show whole threads, not just matching messages
set nm_record        = yes         # index mail you send
set nm_record_tags   = "-inbox +sent"

# \ = ad-hoc search, + = expand the full thread from the archive
macro index \\ "<vfolder-from-query>" "notmuch search"
bind  index,pager + entire-thread

# Tag editing
macro index,pager A "<modify-labels>-inbox -unread<enter>" "archive (notmuch)"
macro index,pager S "<modify-labels>+spam -inbox<enter>"   "mark spam (notmuch)"

Query syntax is notmuch's own: from:alice subject:invoice date:2025.., tag:inbox and not tag:lists, attachment:pdf, "exact phrase". Run notmuch search on the command line to try a query before saving it.

Header cache and IMAP performance

set header_cache = "~/.cache/neomutt/headers/"
set header_cache_backend = lmdb             # fastest common choice; must be compiled in
set header_cache_compress_method = zstd     # if built with zstd; shrinks the cache a lot
set message_cachedir = "~/.cache/neomutt/bodies/"

set imap_qresync = yes        # fast resync on servers supporting QRESYNC (Dovecot, Fastmail, …)
set imap_condstore = yes
set imap_idle = yes           # push notifications instead of polling, where supported

A trailing / on header_cache makes it a directory holding one cache per mailbox, which is the right choice for IMAP.

Other NeoMutt-only conveniences

  • Conditional date formats: %<X?then&else> blocks (which can nest) make a date column show the time for today's mail and the date otherwise: set index_format = "%4C %Z %<[1d?%[%H:%M]&%[%b %d]> %-20.20F %s"
  • index-format-hook: compute a column from a pattern (for example, show a tag only on list mail).
  • cd: change the working directory from inside NeoMutt (where saved attachments go).
  • $new_mail_command: run a command (a desktop notification, for example) when new mail arrives: set new_mail_command = "notify-send 'New mail' '%n new in %f'"
  • Lua (if built with +lua): lua-source ~/.config/neomutt/init.lua gives scriptable access to config and commands. This is useful for logic that's awkward in muttrc syntax.
  • Richer colour objects: individual index columns (index_author, index_subject, index_date, index_flags…) can be coloured separately.

Debugging

neomutt -v                         # version + compile options
neomutt -D                         # dump the effective configuration
neomutt -n -F /dev/null            # start with no config at all
neomutt -d 2 -l ~/neomutt-debug.log   # debug log at level 2 (1–5)

As with Mutt, debug levels of 3 and above can capture credentials and message contents. Delete the log afterwards.

Inside NeoMutt, :set ?var shows a value and :set var=value tries a change live. The <show-log-messages> function displays the recent message history. Bind it to a key (bind index,pager <f12> show-log-messages) for warnings that flash past on the status line.