This document describes how to create dummy mail storage for interimap(1) and/or pullimap(1) development, using Dovecot as IMAP4rev1 server. Start by creating a new temporary directory:

$ BASEDIR="$(mktemp --tmpdir --directory)"

Dovecot configuration

Create a file $BASEDIR/dovecot.conf, which will be used as configuration for the various Dovecot commands (the system configuration will be skipped).

$ cat >"$BASEDIR/dovecot.conf" <<-EOF
	log_path      = "$BASEDIR/dovecot.log"
	ssl           = no
	mail_home     = "$BASEDIR/%u"
	mail_location = maildir:~/Mail

Here are some details on the above:


Dovecot logs to syslog by default. It’s annoying to clutter syslog with test entries, so instead we make it log to a file under $BASEDIR.


Dovecot needs the name of the user to (pre-)authenticate. It is shown in the greeting line, and also used in %-variable expansion. Several doveadm(1) sub-commands have a -u (or -d) option which can be used to determine the username. When this option is not set, the username is taken from the USER environment variable. If that environment variable is unset as well, then the return string of getlogin(3) is used.

Similarly, the user’s home directory is used in (~- and) %-variable expansion. It’s taken from the HOME environment variable when the mail_home setting is left unset in the Dovecot configuration (and not overridden by the user database.

mail_home can therefore be left unset if the HOME environment variable is consistently set to $BASEDIR/$USER. However it’s safer to explicitly set it in the configuration file: otherwise a command run in a non-curated environment might mess up with your own mail storage…


The user’s mail storage resides — in Maildir format — in a directory Mail under their home directory. This is enough if you’re fine with the default IMAP hierarchy delimiter (which depends on the mail format) is used, and if you need a single IMAP namespace. For more complex setups you’ll need one or more namespace {…} block.

Mail storage access

Feel free to point a mail client at the dummy mail storage. To start a pre-authenticated IMAP4rev1 in the test environment for username testuser, list mailboxes, and exit, run:

$ env -i PATH="/usr/bin:/bin" USER="testuser" \
	doveadm -c "$BASEDIR/dovecot.conf" exec imap
S: * PREAUTH [CAPABILITY IMAP4rev1 …] Logged in as testuser
C: a LIST "" "*"
S: * LIST (\HasNoChildren) "." INBOX
S: a OK List completed (0.002 + 0.000 + 0.001 secs).
S: * BYE Logging out
S: q OK Logout completed (0.001 + 0.000 secs).

For mailbox (create, delete, rename) and message (add, flag update) manipulation you can use your mail client, the relevant IMAP4rev1 commands, or simply the doveadm(1) tools. Here is an example using the latter to create a mailbox foo, add a sample message to it, and finally mark it as \Seen.

$ env -i PATH="/usr/bin:/bin" USER="testuser" \
	doveadm -c "$BASEDIR/dovecot.conf" mailbox create "foo"
$ env -i PATH="/usr/bin:/bin" USER="testuser" HOME="$BASEDIR/testuser" \
	doveadm -c "$BASEDIR/dovecot.conf" exec dovecot-lda -e -m "foo" <<-EOF
		From: <>
		To: <>
		Subject: Hello world!
		Date: $(date -R)
		Message-ID: <$(</proc/sys/kernel/random/uuid)>

		Hello world!
$ env -i PATH="/usr/bin:/bin" USER="testuser" \
	doveadm -c "$BASEDIR/dovecot.conf" flags add "\\Seen" mailbox "foo" "*"

Normally dovecot-lda(1) tries to do a userdb lookup in order to determine the user’s home directory. Since we didn’t configure a user database we need to explicitly set the HOME environment variable.

InterIMAP configuration and test

In this example the peers to synchronize are sharing the same Dovecot configuration file $BASEDIR/dovecot.conf. Of course, it’s also possible to use a different configuration on each “server”, for instance in order to specify different hierarchy delimiters, namespaces, or mail storage format.

Create an interimap(1) configuration file to synchronize the local and remote accounts.

$ cat >"$BASEDIR/interimap.conf" <<-EOF
	database = $BASEDIR/interimap.db

	type = tunnel
	command = env -i PATH="$PATH" USER="local" doveadm -c "$BASEDIR/dovecot.conf" exec imap

	type = tunnel
	command = env -i PATH="$PATH" USER="remote" doveadm -c "$BASEDIR/dovecot.conf" exec imap

Run interimap(1) without --watch in order to create the database.

$ env -i PATH="$PATH" perl -I./lib -T ./interimap --config="$BASEDIR/interimap.conf"
Creating new schema in database file …/interimap.db
database: Created mailbox INBOX

You can now run interimap(1) with --watch set, here to one second to observe synchronization steps early.

$ env -i PATH="$PATH" perl -I./lib -T ./interimap --config="$BASEDIR/interimap.conf" \
	--watch=1 --debug

Use instructions from the previous section (substituting testuser with local or remote) in order to simulate activity on either end to synchronize. If you run these commands in another shell, then make sure to re-set the BASEDIR environment variable!

PullIMAP configuration and test

Create a pullimap(1) configuration file with as section [foo].

$ cat >"$BASEDIR/pullimap.conf" <<-EOF
	type = tunnel
	command = env -i PATH="$PATH" USER="testuser" doveadm -c "$BASEDIR/dovecot.conf" exec imap
	statefile = $BASEDIR/

Run pullimap(1) without --idle in order to create the state file.

$ env -i PATH="$PATH" perl -I./lib -T ./pullimap --config="$BASEDIR/pullimap.conf" \
	--no-delivery foo

You can now run pullimap(1) with --idle set.

$ env -i PATH="$PATH" perl -I./lib -T ./pullimap --config="$BASEDIR/pullimap.conf" \
	--no-delivery --idle --debug foo

Use instructions from the previous section in order to simulate activity on the “remote” server (in the relevant mailbox — INBOX by default). If you run these commands in another shell, then make sure to re-set the BASEDIR environment variable!


To remove temporary directories and the message they contain, simply recursively remove the directory $BASEDIR.

$ rm -rf -- "$BASEDIR"