Since I’m running Haswell on my HardenedBSD development laptop, I have to use the VESA driver, which takes up a considerable amount of CPU. I previously ran KDE4 but have recently switched to i3wm. I still relied on kmail, though, since it supported multiple accounts with multiple GPG keys. In order to save precious CPU and RAM, I wanted to switch to mutt. I already knew that its multi-account, multi-gpg key support really sucks, so I devised a plan to use jails.

A few years ago, I toyed with ZFS, jails, and VIMAGE (which I’ll call vnet) to give me a cloud-like infrastructure. The problem is that integrating all the pieces together is kind of a headache. Lots of commands to run in a specific order. Back then, I created a Drupal module to help with that. I’ve since abandoned that project and am now using iocage.


Networking is pretty straight-forward. I have two bridges, bridge0 for bhyve and bridge1 for iocage. I NAT both bridges. Since I often switch between wired, wireless, and tethering over usb, my pf.conf looks a bit weird:

scrub in all
scrub out random-id

nat on re0 from to any -> (re0)
nat on wlan0 from to any -> (wlan0)
nat on ue0 from to any -> (ue0)

nat on re0 from to any -> (re0)
nat on wlan0 from to any -> (wlan0)
nat on ue0 from to any -> (ue0)

pass in all
pass out all

bridge0 is and bridge1 is I only care about bridge1 in this article. bridge1, is given an IP of via /etc/rc.conf:

ifconfig_bridge1="inet netmask up"

Networking is now complete and we can move on to iocage.

iocage configuration

The version of iocage that’s in the ports tree doesn’t support using an existing 11-CURRENT build and the -e flag is broken with the create verb. I had to fetch the sources from GitHub and install a bleeding-edge version of iocage from there. This version still doesn’t technically support using an existing 11-CURRENT build. But at least the -e flag worked.

It look me a little while to figure out, but iocage will create a new heirarchy of zfs datasets. The dataset for your jail isn’t the name of the jail, but rather the UUID iocage has assigned to that jail. As you can see, I have three jails, each with their own UUID:

$ iocage list
JID   UUID                                  BOOT  STATE  TAG
1     30e0a05d-59c1-11e5-974d-f0761c1c06ec  on    up     mutt-hardenedbsd
2     40f606a3-5aeb-11e5-a275-f0761c1c06ec  on    up     mutt-g2
3     c69915d5-58ad-11e5-8955-f0761c1c06ec  on    up     mutt-lattera_at_gmail
$ zfs list -r rpool/iocage
NAME                                                           USED  AVAIL  REFER  MOUNTPOINT
rpool/iocage                                                  3.58G   358G   112K  /iocage
rpool/iocage/.defaults                                          96K   358G    96K  /iocage/.defaults
rpool/iocage/download                                           96K   358G    96K  /iocage/download
rpool/iocage/jails                                            3.58G   358G   104K  /iocage/jails
rpool/iocage/jails/30e0a05d-59c1-11e5-974d-f0761c1c06ec        743M   358G    96K  /iocage/jails/30e0a05d-59c1-11e5-974d-f0761c1c06ec
rpool/iocage/jails/30e0a05d-59c1-11e5-974d-f0761c1c06ec/root   743M   358G  1.44G  /iocage/jails/30e0a05d-59c1-11e5-974d-f0761c1c06ec/root
rpool/iocage/jails/40f606a3-5aeb-11e5-a275-f0761c1c06ec        292M   358G    96K  /iocage/jails/40f606a3-5aeb-11e5-a275-f0761c1c06ec
rpool/iocage/jails/40f606a3-5aeb-11e5-a275-f0761c1c06ec/root   292M   358G  1.00G  /iocage/jails/40f606a3-5aeb-11e5-a275-f0761c1c06ec/root
rpool/iocage/jails/c69915d5-58ad-11e5-8955-f0761c1c06ec       2.57G   358G    96K  /iocage/jails/c69915d5-58ad-11e5-8955-f0761c1c06ec
rpool/iocage/jails/c69915d5-58ad-11e5-8955-f0761c1c06ec/root  2.57G   358G  2.55G  /iocage/jails/c69915d5-58ad-11e5-8955-f0761c1c06ec/root
rpool/iocage/log                                               116K   358G   116K  /iocage/log
rpool/iocage/releases                                           96K   358G    96K  /iocage/releases

I installed and configured the mutt-lattera_at_gmail jail first. So I did a basic full-jail install into /iocage/jails/30e0a05d-59c1-11e5-974d-f0761c1c06ec/root by running these commands:

# iocage create -e tag=mutt-lattera_at_gmail defaultrouter= ip4_addr="vnet1|"
# cd /usr/src
# make installworld distribution \

Please note that the iocage create command will automatically generate the UUID, so your UUID will look different.

I then had to create an empty fstab file for it:

# touch \

The jail is now ready to start up. Boot it up with iocage start mutt-lattera_at_gmail. You can get to the jail’s console by typing iocage console mutt-lattera_at_gmail.

Once I was in the console, I added a user account and set up the jail as I normally would. I installed mutt, gnupg, vim-lite, and a few other packages. I copied my GPG key over and set up offlineimap, since mutt’s imap support isn’t the best. I also set up sshd so that I could ssh in from the host.

Once I had this jail fully set up to my liking, I simply cloned it to create a new iocage jail. Doing it that way saved me time in getting everything set up again. You can clone the jail by running something similar to:

# iocage clone mutt-lattera_at_gmail tag=mutt-hardenedbsd defaultrouter= ip4_addr="vnet1|"

Now I have multiple distinct instances of mutt, each dealing with only on user account. I can use tmux on the host to simply create new windows where I’m ssh’d into the different mutt jails.

Here’s a screenshot of what it looks like:

multiple mutt screenshot