Procmail + Postfix + Virtual Mail User Howto

This post documents my configuration of Procmail for Postfix using virtual mail users.

A virtual user is one who does not have a "real" Linux user account. Virtual mail users are extremely convenient for handling multiple mail accounts and virtual domains. This configuration allows some (privileged) virtual mail users to receive their email via procmail, and all others via postfix's standard "virtual" transport.

I use postfixadmin for virtual mailbox management, so this configuration coincides with my postfixadmin configuration. (This does not cover installation of postfixadmin on Debian.) Specifically, I use the following postfixadmin settings to define the mailbox structure:

// Mailboxes
// If you want to store the mailboxes per domain set this to 'YES'.
// Example: /usr/local/virtual/domain.tld/[email protected]
$CONF['domain_path'] = 'YES';
// If you don't want to have the domain in your mailbox set this to 'NO'.
// Example: /usr/local/virtual/domain.tld/username
$CONF['domain_in_mailbox'] = 'NO';

Note that I have created a user "vmail" to handle virtual mail delivery (see postfixadmin setup):
crnatural:~# grep vmail /etc/passwd vmail:x:150:8:Virtual mailbox:/home/vmail:/sbin/nologin

On my server, this makes the path to a user mailbox to be: /home/vmail/ It is important to remember that procmail must also deliver email following this path structure.

First we configure procmail as an available transport type in postfix's

procmail unix - n n - - pipe
  -o flags=RO user=vmail argv=/usr/bin/procmail -t -m USER=${user} EXTENSION=${extension} /etc/postfix/procmailrc.common

The default transport type for virtual users will be set to be "virtual" in
virtual_transport = virtual
# uncomment the following line to have procmail deliver to all virtual users
# virtual_transport = procmail
procmail_destination_recipient_limit = 1
transport_maps = hash:/etc/postfix/transport

[email protected] procmail
[email protected] procmail

With the above configuration, procmail run the procmail script at /etc/postfix/procmailrc.common for users/domains named in /etc/postfix/transport.

#each user will set his own log file
WS="    "

Note that $HOME contains "/home/vmail," the home directory of the user that handles virtual mail delivery.  Thus, the delivery path matches the virtual mail delivery path.

Bugs: This setup hard-codes the delivery domain, so it can only deliver to email addresses on a single domain.  To deliver to more than one domain, you could pass the full destination email address as an argument to procmail, and write procmail recipes to extract the domain for the SWITCHRC assignment.

(See comments below for code to do multiple-domain delivery in 2 different ways.)

Credits: The idea for this configuration came from a post on the procmail mailing list, procmail Digest, Vol 53, Issue 4 (procmail at lists.RWTH-Aachen dot DE) by "LuKreme"

procmail doesn't make mailbox automatically

Thank you for nice document.

I tried this tips. and I have a problem.

If there is no directory on MAILDIR(=$HOME/$DOMAIN/$USER), procmail doesn't make MAILDIR automatically.
and procmail save mail to DEFAULT(="$MAILDIR/") in this case DEFAULT=/home/vmail.
If there is directory on MAILDIR, procmail work correctoly.

If I set virtual_transport = virtual, postfix automatically makes directory.

Is there any way to avoid this problem ? I want to make mailboxes automatically.


I don't see what your

I don't see what your problem is. You say that when you set virtual_transport = virtual, postfix makes the directory, as you want.

my case

thanks for your reply.

my setting is bellow.
virtual_transport = virtual
transport_maps = regexp:/etc/postfix/transport.procmail
procmail_destination_recipient_limit = 1
transport_destination_recipient_limit = 1
^[email protected]/ procmail
In this case, if there is no directory on
/home/vmail/$user, mail send to DEFAULT.
But if i remove a regrex maps(/^[email protected]/ procmail),
directory (/home/vmail/$user) is created.

I want to make directory automatically when I use procmail with

that makes sence?


Did you make sure that you

Did you make sure that you have regex lookup in your postfix? (Run postconf -m and check for 'regexp' in the output.)

Aside from that, your regexp syntax is missing a forward slash. To match what you are trying to match, you want:
/^[email protected]/ procmail

For more information try

I'm sorry it's typo. I

I'm sorry it's typo.

I wrote
/^[email protected]/ procmail
in transport.procmail

> Did you make sure that you have regex lookup in your postfix? (Run postconf -m and check for 'regexp' in the output.)
yes. postconf -m outputed 'regexp'.

Make sure the directory that

Make sure the directory that holds your mailbox files is writable by the virtual mail user. In my case it is user 'vmail', and my permissions for domain '' look like this:

crnatural:/home/vmail# ls -l
total 4096
drwx------ 4 vmail mail 4096 May 19 2008

I checked the permissions

I checked the permissions and it's no problem.
According to the procmail log, if there is no directory, procmail said bellow.
procmail: Assigning "LASTFOLDER=./new/"
Subject: TEST
Folder: ./new/ 1144
procmail: [1608] Fri May 15 01:38:18 2009

but if there is a directory, like this
procmail: Assigning "LASTFOLDER=/var/vmail/users/nama/Maildir/new/"
Subject: TEST
Folder: /var/vmail/users/nama/Maildir/new/
procmail: [1757] Fri May 15 01:49:07 2009

"Folder:" is different. Is this procmail problem ?

I don't think it is a

I don't think it is a procmail problem, I think it is a procmail configuration problem related to file permissions. Probably you do not have virtual_minimum_uid and virtual_uid_maps set properly. If the virtual mail user ID number is 150 (look in /etc/passwd), the settings (in would be:

virtual_minimum_uid = 150
virtual_uid_maps = static:150

I think you should get postfix working properly with virtual users before attempting to add procmail as a transport. There are plenty of good tutorials on the web on doing that (eg

May be, I understood that

May be, I understood that it's a procmail problem.

checked that problem following steps.

1. making a test procrc, like this.

2, test procrc
bash> procmail -m USER=nama procmailrc < sometext.file
Then, procmail said.
procmail: Couldn't chdir to /home/namai/users/nama/Maildir
and sometext.file delivered to ~/new.
according to testprocmail, the same things happened which i reported before.

3. make "/home/namai/users/nama/Maildir" before procmail executing.
bash> mkdir -p /home/namai/users/nama/Maildir
bash> procmail -m USER=nama procmailrc < sometext.file
and sometext.file delivered to /home/namai/users/nama/Maildir

so, I think it's a procmail problem.
may be, procmail can't recursively make MAILDIR.


I solved the bug by adding

I solved the bug by adding $NEXTHOP=${nexthop} in
procmail unix - n n - - pipe
-o flags=RO user=vmail argv=/usr/bin/procmail -t -m USER=${user} EXTENSION=${extension} NEXTHOP=${nexthop} /etc/postfix/procmailrc.common

then I changed in $NEXTHOP in procmailrc.common

You also can use $NEXTHOP if you have same users in different domains.

I don't know what $EXTENSION is doing, but it's empty in my setup.

Fixing delivery for multiple domains

That's a cool fix! Thanks very much! Did you test that for virtual domains? If your fix works perfectly, it is probably better (simpler) than my solution below.

I also had a need to fix this, and this is how I did it. I modified the procmail service line in as follows:

procmail unix - n n - - pipe
flags=DRO user=vmail argv=/usr/bin/procmail -t -m USER=${user} RECIPIENT=${recipient} /etc/postfix/procmailrc.common

I added what's in boldfaced: RECIPIENT=${recipient}. Note that I removed EXTENSION=${extension}, since I don't use it.

The "O" flag prepends an "X-Original-To: recipient" message header with the recipient address as given to Postfix. Note: for this to work, the procmail_destination_recipient_limit (in must be 1.

Next, I modifed procmailrc.common as follows (this is my full procmailrc.common):
* RECIPIENT ?? .*@\/.*$
#added RECIPIENT variable and we extract domain name
#each user will set his own log file
WS=" "

Notes: The "\/" in this recipe captures everything that matches after it to the variable MATCH. The idea, of course, is to extract the domain name from the email address.