Reading emails in your terminal is fast, highly customizable, productive, and fun. Unfortunately, it can be frustrating to set up. This is a complete guide to take the frustration out of the process.
We will set up reading, writing, and searching email in a terminal with multiple accounts from multiple providers, with both separate and unified views for accounts, for a seamless, provider-agnostic experience. We will also cover some common pain points, like viewing HTML and images. I’m using Ubuntu 22.04, but it should work on other systems as well with minor modifications (yes, including WSL).
What this guide is NOT about is setting up your own email server (although if you want to go down that particular rabbit hole, I can recommend this project).
Why would I even want to do this?
A valid question. As most people, I also used webmail (Gmail) for many years, and when I got new email accounts (first university and then a company) I handled those from Gmail as well. Nevermind that this violated policies; everybody else did it too. At one point, I exported and then deleted a lot of old emails since I was getting low on Gmail space. I finally gave in when I got my fifth account, started taking company policy more seriously, and my university disallowed autoforwarding. At this point I was using three webmails, two from Gmail and one from Outlook, I was unable to easily access some old emails, and it was simply cumbersome.
I have a few arguments in favour of the setup introduced here. Some of these would be valid for a desktop client like Thunderbird as well, some are just GUI vs. TUI arguments, and some assume you have multiple accounts, but they all point in the same direction:
- by having your emails on your disk, you’ll actually own them, with easy-to-manage backups;
- by using a format where all messages go into separate text files, it will be very easy to use all the usual tools with your email that manipulate files and text;
- by using a separate tool for each part of the process (i.e. by applying the Unix philosophy) you’ll be more flexible and more in control of your email process;
- by using a desktop client instead of webmail, you’ll have a familiar experience on all your accounts (even with multiple different providers);
- by choosing the terminal over a GUI, you’ll:
- have a snappier workflow;
- better integration with shell tools (e.g. GPG for signing/encryption or vim for editing);
- send text/plain instead of text/html by default (see here, not that I agree with all the points);
- get a more customizable and faster experience (to be fair, a GUI could be like this, but the keyboard-driven nature of TUI software always end up being superior in this regard).
It’s also pretty fun and educational to dig a little bit deeper into such an important and ubiquitous tool as email.
Configuration guide
Overview of how email works
If you have never done so before, take a look at one of your emails in its raw form. In Gmail, that is “show original”, but here are some examples for other providers. It is just a text file, with some intuitive (e.g. To:, From:) and some less intuitive, maybe provider-specific headers.
Focusing on the terminal client side of things, here’s a breakdown of the different parts needed to write and read email, along with my choice of software:
- An SMTP server that receives and sends emails on your behalf and an IMAP server which allows you to access your emails: Gmail, Outlook, etc., or your own (e.g. with docker-mailserver; JMAP is also a thing, but not very widespread yet).
- Secure authentication with the server, without needing to type your password all the time or to store it unencrypted: pass for passwords and mailctl for OAuth, both of which use GPG for the secure storage.
- A way to fetch emails from the IMAP server: isync (mbsync), which will download each raw email into a separate file (using the so-called maildir format) on your disk and goimapnotify for notification based fetching.
- A “mail user agent” (MUA), that can read the maildir format and compose emails: aerc (other choices here could be e.g. mutt, neomutt, alot, but I’m an aerc fan*).
- A way to send emails to the SMTP server for deliver: msmtp.
- Optionally, a search engine on-par with Gmail’s: notmuch, which we will also configure to provide a unified view over all accounts.
- Optionally, an address book: maildir-rank-addr** or for example khard (or even a combination these).
* and also a contributor, so I’m definitely biased
** this is my creation, so again, I’m biased
Shortcut with aerc IMAP backend
Unarguably, there will be a lot of steps below, so if you just want to read email in your terminal there’s a shortcut you can take by using the IMAP (or the JMAP) backend in aerc. You’ll loose a few things, like offline access, unified view, and search of multiple accounts. You will also be limited a bit more by your provider (for example: Outlook IMAP does not support sorting), but it definitely works, and with the built-in account wizard you can be set up in no time. You might need the authentication part from below and bits of the aerc config. The same accounts I will be configuring below are also configured here for imap as well.
Before we start
The entire configuration will be happening in configuration files. If you don’t have your dotfiles version controlled, it might be a good idea to set that up now. I personally use yadm, but there are many alternatives. For each part, I will provide fully working configuration examples from my own dotfiles. To make sure these do not get out of sync with this tutorial I also tagged the commit at the time of writing.
Also, although many of the programs we need can be installed directly with apt install
, the repositories are usually quite out of date, so many of them will
need to be built from source. To build everything you will need some build
tools (sudo apt install build-essential
) and a go environment.
Authentication
Depending on your provider(s), you will either need to use a password or an OAuth token to log in. In any case, it’s not a good idea to store these as unencrypted in a configuration file or anywhere on your computer, so as a first step, make sure you have a working GPG installation and a GPG key you can use to encrypt. Since this can be a pretty deep rabbit hole on its own, and there are many tutorials, this is the one thing I will not go into how to configure, but rather link to a tutorial. Depending on your threat model, there’s a few different ways you can set this up: for example on my personal laptop I have a separate key for encrypting passwords and tokens, and the key’s password is saved in the keyring, but on my work laptop, I have my GPG key on a Yubikey.
The important thing here is that all of the other programs we’re going to use support specifying either the password directly or a command whose output will be used as a password (or authentication token).
A note on Gmail: you can create an “application specific password” for your account somewhere in the google ecosystem, or you can use OAuth. I suggest the latter.
Password authentication (passwordstore)
Let’s set up pass for this:
sudo apt install pass
pass init [your gpg key id]
pass insert email/youremail
pass init
will initialize a folder at ~/.password-store
and pass insert email/youremail
will encrypt a file at ~/.password-store/email/youremail
with the password passed to it. Feel free to change the actual path. Verify that
pass show email/youremail
prints your password.
You can also set up git with pass git init
, after that pass git X
will take
any git command X
. Since everything is encrypted, it should be safe to upload it
somewhere, although probably a public git repository is not the best idea.
OAuth (mailctl)
Example configs:
For this, we will use mailctl
. The easiest way is to grab the latest
release and put it on your path. We
need to create two configuration files for mailctl.
In the config.yaml
you will need to change the paths (i.e. change fbence
to
your username) and change the GPG key to your own on line 10. From the example
services.yaml
you can safely copy google
and microsoft
verbatim, they
will identify mailctl
as a well known open-source email client. If you have
a different provider, you might need to ask on the mailctl mailing
list or see the github
issues for the exact config to use.
By default, the usage is pretty simple: mailctl authorize google ferdinandybence
to authorize using the google
service in services.yaml
with ferdinandybence@gmail.com. When the command starts, open the link printed
by mailctl in your browser, and log in as usual. Check if mailctl access ferdinandybence
successfully returns a token.
If you have a personal account, this should work. If you have a company address,
for Google you will need to use the extra flag --company
and for Microsoft,
you will need to do a little
dance
with the login:
- click “Sign in with another account”
- click “Sign-in options”
- click “Sign in to an organisation”
- put in the correct domain name which matches your
company.email
address above
Syncing mail: isync/mbsync
This is a bit confusing, since the software is called isync
but the
executable is called mbsync
due to a package name collision.
Building from source
The currently packaged version on Ubuntu is 1.4.4 which is too old, so we will
need to build from source. Note that a newer version hasn’t been released yet because of two regression. Usually this is not an issue, but I have encountered on of them when running against davmail
, but reverting a single commit solves the issue in that case as well. We’ll also need to set up some prerequisites:
sudo apt install libsasl2-dev sasl2-bin
# rest taken from [here](https://unix.stackexchange.com/a/632794/82390)
git clone https://github.com/moriyoshi/cyrus-sasl-xoauth2.git
# Configure and make.
cd cyrus-sasl-xoauth2
./autogen.sh
./configure
# SASL2 libraries on Ubuntu are in /usr/lib/x86_64-linux-gnu/; modify the Makefile accordingly
sed -i 's%pkglibdir = ${CYRUS_SASL_PREFIX}/lib/sasl2%pkglibdir = ${CYRUS_SASL_PREFIX}/lib/x86_64-linux-gnu/sasl2%' Makefile
make
sudo make install
Otherwise, it uses a pretty regular build scheme. Personally, I don’t like to install stuff like this as root, but feel free to change the prefix.
git clone https://git.code.sf.net/p/isync/isync
cd isync
./autogen.sh
./configure --prefix=$HOME/.local
make
make install
Verify that running mbsync --version
returns 1.5.0 or higher.
Configuration
Example configs:
Configuring isync
is far from trivial, unfortunately. You will need to create
a ~/.config/isyncrc
file.
The logic of the config file is like this:
There’s a setup phase (this account uses password authentication), where you
define connection properties, define a remote (bence-remote
) and define the
local maildir store (bence-local
).
IMAPAccount bence
Host mail.ferdinandy.com
User bence@ferdinandy.com
AuthMechs LOGIN
PassCmd "pass show email/ferdinandy/bence | head -n1"
TLSType IMAPS
CertificateFile /etc/ssl/certs/ca-certificates.crt
IMAPStore bence-remote
Account bence
MaildirStore bence-local
SubFolders Verbatim
Path ~/.mail/bence/
Inbox ~/.mail/bence/Inbox
Important: mbsync will not create the Path
for you, so in the above
example, I need to mkdir ~/.mail/bence
before running mbsync.
After this you define channels between the local and the remote, which can then be grouped together for easy referencing. My logic was to use these channels to a) get standardized names for the important folders (e.g. Trash should be Trash independent of language and provider, and remove all the irritating [GMAIL] prefixes), and b) to create “fast” channels, which largely means that they exclude my usually very large Sent and Archive folders.
An easy way to set these up is to have a catch-all channel, but then any extra channel needs to be carefully excluded from it. If you do a renaming like in the below example, then both the far (server side) and near (local) name needs to be excluded; otherwise you will end up with a lot of duplicated mail.
Channel gmail-labels
Far :gmail-remote:
Near :gmail-local:
Patterns * ![Gmail]* !INBOX !Archive* !Drafts !Sent* !Spam !Trash
Channel gmail-archive
Far ":gmail-remote:[Gmail]/Összes levél"
Near ":gmail-local:Archive"
Patterns *
Some words of caution:
- A LOT has changed in isync since 1.4.4 configuration wise, so instead of relying on the internet, read the manpages (
man mbsync
). - It is entirely possible to create loops when you use channels to have different remote and local names. If you have wildcard and non-wild card channels, carefully exclude any folders from the wildcard channel you are separately syncing in another channel.
- If things go haywire and you want to restart from scratch you need to also delete
~/.local/state/isync/
.
If you have configured everything, then try running mbsync -a
and then
looking into your ~/.mail/
folder.
Caeml might help you out if you quickly
want to look at what email you have (go install github.com/ferdinandyb/caeml@latest
). If you have issues you can’t figure out,
I recommend writing to isync-devel@lists.sourceforge.net, which despite the
name is also the channel to ask for help.
Note that some providers may have a daily limit, so if you have a large enough
mailbox, the initial syncing may hit hard limits, and you’ll need to stretch it
out over a couple of days (Gmail has a pretty low daily limit, for example). You might also need to set PipelineDepth 1
in your isync
configuration if your provider needs you to throttle bandwidth.
Sending: msmtp(q)
Installation
sudo apt install msmtp
- copy the msmtpq script to somewhere on your
$PATH
- copy the msmtp-queue script to somewhere on your
$PATH
Setup
We need to modify the AppArmor profile to be able to use mailctl
with msmtp:
sudo vim /etc/apparmor.d/usr.bin.msmtp
, find the profile helpers part and
make sure you have pass
and mailctl
included like in this example:
profile helpers {
#include <abstractions/base>
/{,usr/}bin/bash mr,
/{,usr/}bin/dash mr,
/tmp/ rw,
owner /tmp/* rw,
/usr/bin/secret-tool PUx,
/usr/bin/gpg{,2} PUx,
/home/fbence/.local/bin/mailctl{,2} PUx,
/usr/bin/pass PUx,
/usr/bin/head PUx,
/usr/bin/keyring PUx,
/{,usr/}bin/cat PUx,
}
Once done with the modification, run sudo apparmor_parser -r /etc/apparmor.d/usr.bin.msmtp
Create /etc/logrotate.d/msmtp
with similar content (change the username):
/home/fbence/.msmtp.log /home/fbence/.msmtp.queue.log {
rotate 9999
weekly
compress
notifempty
missingok
}
Configuration
Example config:
After setting up isync
this should be a breeze.
Test if it works by running (bence is the account I’m sending from):
echo -e "Subject: Regards\n\nSending regards from Terminal." | msmtp -a bence recipient@mail.com
msmtpq
can be used as a drop-in replacement for msmtp
. What it does, is that it will try to send the email with msmtp, but if it fails, it will place it in ~/.msmtp-queue
. Calling msmtp-queue -r
will try to send emails again, that were placed in the queue. You can try this by disconnecting from the internet and trying the previous echo command with msmtpq
.
notmuch
Notmuch can be used directly as a backend for several email clients, including alot, dodo, Emacs, vim and (more importantly for us) aerc. While it can be used on its own, we are going to use it for its search index, and ability to seamlessly operate over multiple accounts' maildir folder. This will provide us with the ability to search all of our email regardless of account, and to show a unified overview of certain folders, e.g. a unified inbox. If you are only setting this up for a single account, I still recommend using notmuch for its search capabilities.
Installation
git clone https://git.notmuchmail.org/git/notmuch
cd notmuch
./configure --prefix=$HOME/.local
make
make install
Optionally, if you plan on doing some scripting, the notmuch python bindings can come in handy:
python3 -m pip install notmuch
(there’s a newer notmuch2
library, but my scripts are still with the older
one if you want to copy anything).
Configuration
Example config:
The configuration file is probably best placed at
$HOME/.config/notmuch/default/config
(although multiple databases can also be set up). You basically need to tell where your
maildir folders are, and you’re good to go, but it is a good idea to make it
ignore .uidvalidity
files, which are created by isync
, and to set it to
sync maildir flags (e.g. read/unread). There are quite a lot of tweaks you
could make, but since we’ll not be using every capability, we can leave it
fairly simple.
[database]
path=/home/fbence/.mail
[new]
tags=inbox;unread
ignore=.uidvalidity
[search]
[maildir]
synchronize_flags=true
Once you are done, run notmuch new
, which could take some time if you have
a lot of email. If everything goes well, you can try a search on the
command line, for example notmuch search from:example@email.com
.
Address book: maildir-rank-addr
An address book is an essential feature, but aerc (and other similar email
clients) does not come with one, instead, you choose your own. maildir-rank-addr
is my
solution for this, which automatically generates a ranked address book, with
a few default filters to clear noreply addresses and such. You can then search
this really fast with your favourite grep tool (I recommend
ugrep). There are other, hand-curated
solutions as well, like khard, and also tools that can combine the output from
several similar tools (see the
README for more details).
Installation
go install github.com/ferdinandyb/maildir-rank-addr@latest
Try running maildir-rank-addr --maildir=~/.mail
where you should obviously change
~/.mail
to wherever your maildirs are located. This should create the file
$HOME/.cache/maildir-rank-addr/addressbook.tsv"
.
Searching would look something like this (replace searchterm):
ugrep -jP -m 100 --color=never [searchterm] ~/.cache/maildir-rank-addr/addressbook.tsv
Configuration
Example configs:
Two things can be configured. First, it’s probably a good idea to make a helper script which makes the above ugrep query a bit easier to use on the CLI.
Secondly, you can configure maildir-rank-address itself of course, for example by moving the --maildir
flag into a configuration file (by default at
$HOME/.config/maildir-rank-addr/config
). For aerc a minimal config like
maildir = "/home/fbence/.mail"
addresses = [
"bence@ferdinandy.com",
"ferdinandy.bence@ttk.elte.hu",
"priestoferis@gmail.com",
"bence.ferdinandy@pharmahungary.com",
"bence.ferdinandy@formsense.com"
]
is probably enough, but there are a few other things you can do, like setting up extra filters for exclusion. Check the repository README for ideas on how to integrate it with vim.
Automation
Now that we have set up everything, we need to automate a few things, which I did with systemd
services. To get incoming emails as soon as possible, I use goimapnotify
to run isync
only the Inbox channels of my appropriate account.
goimapnotify
Example configs:
goimapnotify uses the IMAP IDLE extension to watch for new messages on the server and can be configured to run isync
whenever new mail arrives. This insures that incoming email arrives in your maildir as soon as possible.
To install it, run go install gitlab.com/shackra/goimapnotify@latest
.
Next we are going to create a template service for it in $HOME/.config/systemd/user
with a file named goimapnotify@.service
, and the following content (adjust as needed):
[Unit]
Description=start goimapnotify for various mailboxes
# OnFailure=status-email-user@%n.service
After=local-fs.target
After=network.target
[Service]
Restart=always
RestartSec=300
ExecStart=/home/fbence/go/bin/goimapnotify -conf /home/fbence/.config/emailconfiguration/%i.conf
[Install]
WantedBy=default.target
# https://usher.dev/posts/my-email-setup/
The magic here is that if you enable and start it like this:
systemctl --user enable --now goimapnotify@elte_imapnotify.service
then the elte_imapnotify
part will be substituted on the service file at the %i
, so you can create a configuration file for each of your accounts, but you only need one systemd service file to run them all. It’s possible to configure goimapnotify to not only run isync, but also to run notmuch after syncing, e.g.:
{
"host": "outlook.office365.com",
"hostCmd": "",
"port": 993,
"tls": true,
"tlsOptions": {
"rejectUnauthorized": true
},
"username": "ferdinandy.bence@ttk.elte.hu",
"usernameCmd": "",
"password": "",
"passwordCmd": "mailctl access ferdinandy.bence@ttk.elte.hu",
"xoauth2": true,
"onNewMail": "mbsync elte-inbox",
"onNewMailPost": "notmuch new",
"wait": 20,
"boxes": [
"Inbox"
]
}
Technically, it would be possibly to create a single config file with all accounts, but I do not recommend it, as unfortunately, a failure in one account fails the entire goimaptnotify
process, essentially propagating the error to all accounts.
General syncing
Example configs:
- ~/.config/systemd/user/mailsync-high.service
- ~/.config/systemd/user/mailsync-high.timer
- ~/.config/emailconfiguration/mailsync-high
- (and similarly for medium and low)
For everything else, I have created three scripts, depending on the frequency I want the specific service to run. The high frequency script runs every minutes, the medium frequency script runs every 15 minutes, and the low frequency script runs every 6 hours.
In $HOME/.config/systemd/user
create a mailsync-high.service
file
[Unit]
Description=Mail Sync high frequency
#OnFailure=status-email-user@%n.service
After=local-fs.target
After=network.target
[Service]
Type=oneshot
ExecStart=/home/fbence/.config/emailconfiguration/mailsync-high
which will be running the script specified at ExecStart
,
and a mailsync-high.timer
file:
[Unit]
Description=Mail Sync high frequency timer
[Timer]
OnBootSec=2min
OnUnitActiveSec=1m
[Install]
WantedBy=default.target
and run systemctl --user enable --now mailsync-high.timer
to enable and start. To list all running timers, run systemctl --user list-timers
.
The high-frequency one is responsible for keeping notmuch up-to-date, sending emails as soon as I connect to the internet if I was offline, and syncing my most used folders quickly, so my phone is never really out-of-sync with my computer. The medium one just does a full maildir sync, while the low frequency one runs the address book update and my flavour of archiving, which archives by yearly folders, but only email that are older than a year, younger emails stay in a flat archive.
aerc specific note
Aerc has hooks, that can run even external commands on certain events. Not many hooks are implemented currently, but there’s a startup, a shutdown and a mail-recieved hook at the time of writing. Arguably, neither the goimapnotify services nor the high-frequency sync service makes any sense if you are not actively looking at or manipulating your email, so these hooks, in theory, could be used to start and stop the respective services. I haven’t explored this yet.
Actually starting to use email: aerc
The man pages for aerc are quite good, so I suggest reading them after the initial setup. There’s also an aerc wiki. Furthermore, if you don’t know how to collaborate with git+email (instead of the now more common PR/MR-based workflows), this might be an interesting read: https://git-send-email.io/. Aerc has first-class support for this workflow.
Installation
Aerc is still actively developed, so I suggest building from source (modify installation destination according to your tastes):
git clone https://git.sr.ht/~rjarry/aerc
cd ./aerc
make install PREFIX=$HOME/.local
Configuration
Example configuration files:
- Account configuration: ~/.config/aerc/accounts.conf
- General configuration: ~/.config/aerc/aerc.conf
- Keybindings definitions: ~/.config/aerc/binds.conf
- Mapping for folders in notmuch: ~/.config/aerc/notmuchmap.conf
maildir for each email account you have
In ~/.config/aerc/accounts.conf
you need to create an entry for each of your
accounts you synced with isync. The most important part here is the first three
lines so that we use the correct maildir and that we send with the correct
msmtp account:
[bence]
source=maildir:///home/fbence/.mail/bence
outgoing=msmtpq -a bence
from=Bence Ferdinandy <bence@ferdinandy.com>
default=Inbox
folders-sort=Inbox
copy-to=Sent
pgp-key-id=D92449B0F2D9363A2DE3260B207C0A2055199A65
pgp-auto-sign=false
pgp-opportunistic-encrypt=false
signature-file=/home/fbence/.config/emailconfiguration/ferdinandy_signature.txt
If you only have a single account, then I suggest setting up a notmuch://
, instead
of a maildir://
account, with maildir-store
(see below).
Unifying notmuch account
We’re going to add an extra account, that will use the notmuch backend, but
we’ll also tell it to use some maildir capacities (maildir-store
) which will
allow us, amongst other things, to specify a copy-to for sending here as well. The query-map
file is
used to define notmuch queries, which will then show up as virtual folders.
This account and the virtual folders will be used for a unified view and
a unified search later. If you only have a single account I actually suggest to
set only notmuch up. If you don’t create a query-map, but add a maildir-store
then you’ll have a regular maildir account, but with the power of notmuch
searching. Keep in mind when mixing notmuch with direct maildir access, that instead of showing the individual files like the maildir backend, notmuch will only show individual messages, even if you have multiple copies of it in different folders (or accounts).
[notmuch]
source=notmuch:///home/fbence/.mail/
maildir-store=/home/fbence/.mail/
outgoing=msmtpq -a bence
copy-to=bence/Sent
query-map=/home/fbence/.config/aerc/notmuchmap.conf
default=Inbox
folders-sort=Inbox
from=Bence Ferdinandy <bence@ferdinandy.com>
pgp-key-id=4A3641B69E9984012F396B31E3B23486302F2FA9
pgp-auto-sign=true
pgp-opportunistic-encrypt=false
signature-file=/home/fbence/.config/emailconfiguration/ferdinandy_signature.txt
Filters
The main feature of aerc is how close it lives to your shell. It runs everything in an embedded terminal (and can also open one on a new tab) so you can utilize all your favourite tools.
In ~/.config/aerc/aerc.conf
you can
configure a bunch of things. Most important of these is the [filters]
section, where you can specify what commands certain parts of your email should
be piped into, based on mimetypes or regexes on headers. Aerc comes with a few filters installed
already, but you can for example add one for docx or pdf. Or even something like this:
to,tlsrpt@ferdinandy.com=gunzip -c - | jq . | bat -fP --file-name "report.json" --style=plain
which filters for a specific type of report which is a zipped json, and shows the contents nicely formatted, without needing to go through downloading and opening the zip.
You can also use the :pipe
command to manually pipe a message part if you
happen to need it on the fly.
Editor
You can also configure your editor, either by passing it extra parameters in
aerc.conf
or if you are using vim, by creating your own rules for the mail
filetype in ~/.vim/after/ftplugin/mail.vim.
Binds
The default bindings of aerc are pretty decent. My additions revolve around
having pairs of gX
and mX
bindings which will either take me to
a specified folder or move the selected message to a specified folder, and
switching between my account tabs. Note that some of these make sense to set
under both [message]
and [view]
:
gi = :cf Inbox<Enter>
g1 = :cf 1_megválaszolni<Enter>
m1 = :read<Enter>:move 1_megválaszolni<Enter>
mr = :read<Enter>:move reports<Enter>
tb = :change-tab bence<Enter>:cf Inbox<Enter>
te = :change-tab elte<Enter>:cf Inbox<Enter>
Some other noteworthy mentions are archiving with e
with and archiving the entire thread with E
:
e = :read<Enter>:archive flat<Enter>
E = :unmark -a<Enter>:mark -T<Enter>:read<Enter>:archive flat<Enter>
and archiving emails when replying to them by default:
[compose::review]
# Keybindings used when reviewing a message to be sent
y = :send -a flat<Enter>
Y = :send<Enter>
Unified search and account view
This solution was tailored to my needs and is not perfect, but it works more of less. It’s also an example of why it might be worth to go through the hassle of syncing maildirs instead of using imap directly.
Searching
The search part is pretty trivial: since all my accounts are maildir and under
~/.mail
notmuch indexes them all so any search in the notmuch account
searches all my mail. I actually remapped the search bind to change to my
notmuch account tab and start a search there:
/ = ":change-tab notmuch<Enter>:cf "
Since the notmuch account is one specific account, when replying I need to pay
attention to using :switch-account
to change to the appropriate one if it is
important. A possible alternative to :switch-account
could be to turn even
the regular accounts into notmuch+maildir, but I haven’t explored this yet.
Unified viewing
The idea is straightforward, although setting it up requires a lot of typing so
I actually wrote
a script
to generate the required
query-map.
The problem I wanted to solve is to have all my separate Inbox folders from my
accounts show up in a single folder in the notmuch account. Instead of trying
to come up with clever tags, we can leverage the path:
search key in notmuch
with returns messages under the given path, relative to the maildir folder
configured for notmuch. This way any change done in the maildir accounts is
trivially translated to the notmuch view. This can of course be done for any
number of folders and you wouldn’t even need to call them the same in all your
accounts, although having matching names makes writing binds much easier.
If you open the query-map file you’ll notice that all the path:
queries are
ended with an and not tag:aerc
. I use this to signal that I started an
operation on the messages from aerc, that needs to be finished, which leads us
to the next part.
Moving between the unified folders
Although the notmuch+maildir account of aerc can actually move files between
maildir folders if I wanted to, it would be complicated. To move a message from my unified Inbox to my
unified 2_rám_vár
(= 2_waiting_for_me
) folder I would need to move it the
correct account’s maildir folder called 2_rám_vár
. To be able to reuse similar
bindings as in the simple maildir accounts for moving and to not have to manually construct
the correct paths every time, I instead tag the message in aerc with the tag 2_rám_vár
and aerc
and have mailsync-high
run a sync
script.
This script finds all messages tagged with aerc
, figures out which account
the message is from, moves the message to the correct folder and then removes
both tags.
Update:
Since 0.16.0-170-g8922d95ccb00 with the {{Filename}}
template and
the maildir-store
setting this can be solved much easier, as long as your
aerc account names and the maildir folder names are the same. For example, if
your account name in aerc is bence
and the appropriate maildir folder is
~/.mail/bence
, then
:mv {{index (.Filename | split ("/")) 4}}/Archive <Enter>`
will move it to the correct folder. See here how I migrate how I migrated.
Similarly, sending with the appropriate account can be solved with:
a = :reply -acqA {{index (.Filename | split ("/")) 4}}<Enter>`
Html email
Any sane provider, even if they have html as default, should send also
a text/plain version of the email. A sane provider will also only add the bare
minimal markup to the html version. Unfortunately you’ll soon notice, that this
is not always the case. Aerc has a nice filter for html already installed with
it, but sometimes (usually emails from large companies that are very
unnecessarily heavily branded) I just open it directly in my browser (O
). In my
experience, most of my email is just text, so this slow-down with heavy html
emails is easily overcompensated by the ease of handling the others.
I sometimes also reply to them, that the unnecessary use of images makes their content less accessible and sometimes they listen :)
Dealing with images
The default way to view images is to open them in an external program. There’s a shortcut so it’s not terrible, but it is not great either. There are some workarounds, and it will hopefully improve in the near future. At this point I’ll need to get a little bit technical.
Currently there are two ways to show images in a terminal: the kitty terminal graphics protocol and sixels (some work is being put into an alternative). Ideally, you’d run aerc with a terminal emulator supporting one or the other and just use something as a filter for images that outputs the proper format. If you set
image/*=catimg -w$(tput cols) -
as a filter you can get a very low resolution feeling for how this could work.
The problem unfortunately is that aerc uses tcell-term
which uses tcell
and
tcell
doesn’t support either protocol, so you can’t just cat an image into
the embedded terminal of aerc. What you can do though is cat the image on
top of aerc, by communicating directly with your terminal emulator. This works,
but is a bit cluncky, because you need to make sure your emulator also cleans
up the image and hands back control to aerc. The below monstrosity is a working
example with kitty
where the image is opened, but then you need to press an
extra key to close it:
image/*=convert - -resize 1000x500\> - | kitty +kitten icat --stdin=yes --silent --transfer-mode stream --hold --place=120x120@0x7 && kitty +kitten icat --silent --transfer-mode stream --clear
Final notes
On the go
If you have Android, then FairEmail is a pretty solid choice for handling all your accounts from a single app. As long as you stick to IMAP folders for sorting your emails, you should be fine.
Some useful links that helped me or have slightly different setups
- https://usher.dev/posts/my-email-setup/
- https://wiki.archlinux.org/title/isync
- https://jonathanh.co.uk/blog/mutt-setup/
- Robin Jarry’s (aerc maintainer) dotfiles
- Drew DeVault’s (aerc’s original author) blog on his setup
- Deduplicate accidents with isync: https://kdeldycke.github.io/mail-deduplicate/
Community, help, feedback
During my journey, I was in contact with several maintainers, contributors and enthusiasts who helped me figure things out. The IRC channels and mailing lists are full of helpful people, so if you get stuck, I encourage you to reach out to them. You can also reach out to me in email or below in the comments, especially if I managed to mess up something.
Acknowledgments
A heartfelt thank you to Robin Jarry, Tim Culverhouse, Koni Marti, Moritz Poldrack, Peter Dobsan, and Oswald Buddenhagen for helping out with my configuration, whipping up patches when I was missing something and guiding me through some of my own patches. Special thanks to Moritz Poldrack, Gergely Libertiny and inwit for reading the draft and providing helpful comments and fixes to this tutorial.