Mutt, Procmail, GnuPG, and Postfix MX

Category: Computing Tags: , , , , , ,

At first, I was lazy. I used Google’s Gsuite for sogubsys, piggy-backing off sogub services. It was a hack and I didn’t feel good about it. Fortunately, in a tech-kern post Mouse stated that he couldn’t or wouldn’t receive emails that include a Gmail path. Thanks, Mouse.

That simple statement was enough for me to do what I actually wanted to do: run my own MX and handle my own mail. I’ve done it before, but it was a long time ago.

Here are the requirements:

  • Use Postfix as the MX for
  • Use Mutt as a local client.
  • Use Maildir instead of Mailbox.
  • Use GnuPG to support encryption, signing, decryption, and verification of keys and signatures in Mutt, using both attachments (new) and inline (old) formats.
  • Subscribe to a lot of NetBSD mailing lists and easily be able to discern threads and what list it is a part of in Mutt.
  • Backup all incoming email to the previously used Gmail address.
  • Setup a NetBSD stable server (7.1 at this time).
  • Be able to check email from my Android phone.

All of these things are simple. I’ll detail what I did, so you can do it, too.


Server Setup

I decided to use as a service provider due to their ability to allow a custom ISO for installation of a VPS, ease of use, stability, and I already had experience with them. Use whatever service you like, but I like Vultr.

I provisioned a server with latest NetBSD 7.1. I ensured pkgin was installed and ssh was started. Network was configured as hostname with an IPv4 IP and IPv6 subnet, and reverse DNS on both (all in the Vultr web control panel).

I installed packages I’ll later need:

# pkgin in mutt procmail gnupg mb2md emacs25-nox


Postfix Setup

Postfix is installed by default. I configured it to support receiving email for, itself, to listen on all interfaces, and bcc all email to another email address.



smtpd_banner = ESMTP =^..^= RIP Jonesy
biff = no
append_dot_mydomain = no
readme_directory = no
myhostname =
myorigin =
mydestination =,, localhost, localhost.localdomain
relayhost =
mynetworks = [::ffff:]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
luser_relay = chris
local_recipient_maps =
recipient_bcc_maps = hash:/etc/postfix/recipient_bcc_maps
disable_vrfy_command = yes
default_process_limit = 100
smtpd_client_connection_count_limit = 10
smtpd_client_connection_rate_limit = 30
queue_minfree = 20971520
header_size_limit = 51200
message_size_limit = 10485760
smtpd_recipient_limit = 100
smtpd_sender_restrictions = reject_unknown_sender_domain
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
home_mailbox = Maildir/
mailbox_command = /usr/pkg/bin/procmail -a "$EXTENSION" DEFAULT=$HOME/Maildir/ MAILDIR=$HOME/Maildir

Activate new configuration. Postfix is defaulted to start in /etc/defaults/rc.conf, but I put it in /etc/rc.conf anyway (in case that changes in the future).

# echo "postfix=YES" >> /etc/rc.conf
# postmap /etc/postfix/recipient_bcc_maps
# /etc/rc.d/postfix restart


DNS Setup

I then updated DNS for the zone, adding A and AAAA records for I then removed the google MX records and added a single MX record pointing to Additionally, I added an SPF TXT record.

ripley 3600 IN A
ripley 3600 IN AAAA 2001:19f0:5001:43c:3d71:ba18:a8f1:6b8
@ 3600 IN MX 0
@ 3600 IN TXT "v=spf1 mx ip4: -all"


Procmail Setup

This part took the longest because I was battling with the :0* options trying to get it right. Basically I only want to X-Label all the things and Mutt will then display that label next to the subject.



# Twitter: @sogubsys
* ^From.*twitter
| /usr/pkg/bin/formail -I "X-Label: twitter"

# netbsd-advocacy
* ^List-Id: netbsd-advocacy.*
| /usr/pkg/bin/formail -I "X-Label: netbsd-advocacy"

# netbsd-announce
* ^List-Id: netbsd-announce.*
| /usr/pkg/bin/formail -I "X-Label: netbsd-announce"

# security-announce
* ^List-Id: security-announce.*
| /usr/pkg/bin/formail -I "X-Label: security-announce"

# source-changes-d
* ^List-Id: source-changes-d.*
| /usr/pkg/bin/formail -I "X-Label: source-changes-d"

# source-changes-digest
* ^List-Id: source-changes-digest.*
| /usr/pkg/bin/formail -I "X-Label: source-changes-digest"

# tech-crypto
* ^List-Id: tech-crypto.*
| /usr/pkg/bin/formail -I "X-Label: tech-crypto"

# tech-kern
* ^List-Id: tech-kern.*
| /usr/pkg/bin/formail -I "X-Label: tech-kern"

# tech-perform
* ^List-Id: tech-perform.*
| /usr/pkg/bin/formail -I "X-Label: tech-perform"

# tech-pkg
* ^List-Id: tech-pkg.*
| /usr/pkg/bin/formail -I "X-Label: tech-pkg"

# tech-security
* ^List-Id: tech-security.*
| /usr/pkg/bin/formail -I "X-Label: tech-security"

# source-changes-full
* ^List-Id: source-changes-full.*
| /usr/pkg/bin/formail -I "X-Label: source-changes-full"


Mutt Setup

This part wasn’t too bad. I kept having to add things to the config as I noticed I missed things, but here is what I got.

I used the code for explicitly adding labels and filtering by labels from


set realname = 'Chris Humphries'
set editor = 'emacs'
set index_format="%4C %Z %{%b %d} %-15.15L %?M?(#%03M)&(%4l)? %?y?(%.20Y) ?%s"
set visual = 'emacs'
set from = ''
set pgp_decode_command="gpg %?p?--passphrase-fd 0? --no-verbose --batch --output - %f"
set pgp_verify_command="gpg --no-verbose --batch --output - --verify %s %f"
set pgp_decrypt_command="gpg --passphrase-fd 0 --no-verbose --batch --output - %f"
set pgp_sign_command="gpg --no-verbose --batch --output - --passphrase-fd 0 --armor --detach-sign --textmode %?a?-u %a? %f"
set pgp_clearsign_command="gpg --no-verbose --batch --output - --passphrase-fd 0 --armor --textmode --clearsign %?a?-u %a? %f"
set pgp_encrypt_only_command="pgpewrap gpg --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust --encrypt-to 0xC9C40C31 -- -r %r -- %f"
set pgp_encrypt_sign_command="pgpewrap gpg --passphrase-fd 0 --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust --encrypt-to 0xC9C40C31 -- -r %r -- %f"
set pgp_import_command="gpg --no-verbose --import -v %f"
set pgp_export_command="gpg --no-verbose --export --armor %r"
set pgp_verify_key_command="gpg --no-verbose --batch --fingerprint --check-sigs %r"
set pgp_list_pubring_command="gpg --no-verbose --batch --with-colons --list-keys %r"
set pgp_list_secring_command="gpg --no-verbose --batch --with-colons --list-secret-keys %r"
bind pager  previous-page
bind pager  next-page
color normal white black
color hdrdefault blue black
color indicator white blue
color markers red black
color quoted cyan black
color status white blue
color error red white
color underline yellow black
mono quoted standout
mono hdrdefault underline
mono indicator underline
mono status bold
set sort=threads
set sort_browser=date
set mbox_type=Maildir
set folder="~/Maildir"
set mask="!^\\.[^.]"
set mbox="~/Maildir"
set record="+.Sent"
set postponed="+.Drafts"
set spoolfile="~/Maildir"
message-hook '!(~g|~G) ~b"^-----BEGIN\ PGP\ (SIGNED\ )?MESSAGE"' "exec check-traditional-pgp"

# From
macro index y "set editor=\"~/bin/editlabel append\"\n\
set editor=emacs\n" "Append label"
macro pager y "set editor=\"~/bin/editlabel append\"\n\
set editor=emacs\n" "Append label"
macro index Y "set editor=\"~/bin/editlabel menu\"\n\
set editor=emacs\n" "Edit labels"
macro pager Y "set editor=\"~/bin/editlabel menu\"\n\
set editor=emacs\n" "Edit labels"
macro index \Cy "~y " "Limit view to label"



Mutt main screen in normal terminal


Mutt PGP signed message in normal terminal


Mutt PGP signed message in JuiceSSH terminal