Let’s do Postfix slowly and properly – Part 4: Virtual domains

Replacing local accounts with virtual accounts – and understanding the difference

Since the first post in my Postfix series, I have been working with a local setup. In this post I will make the switch to a virtual setup, i.e. one in which email accounts are independent of system accounts.

In a virtual configuration, I have to tell Postfix what accounts I have on any given domain, where to put email for an account etc. in contrast to local delivery where users are defined by the system and delivery is by default to a file in /var/mail. In order to create this kind of setup I will have to jettison most of the settings I have worked on so far. On the plus side it will still feel very familiar as virtual and local settings often follow the same basic pattern and nomenclature. In addition, as I have said before, familiarizing yourself with a local setup helps you understand how to properly distinguish between the two kinds of setup.

Deactivate the local setup

First I need to deactivate some of my local settings so as not to interfere with my new virtual setup. Basically I am going to make it so that local mail is actually local, that is deals with mail addressed to users on the local machine. I would suggest saving a backup copy of your current setup so that you can always revert.

Here’s a fairly innocuous local setup:

myhostname = ACORP
mydomain = $myhostname
myorigin = $myhostname
mydestination = $myhostname, localhost

This setup can coexist very nicely with a virtual domain setup for my internet domains. Mail delivery in Postfix is handled by delivery agents, one of which is called local and another is called virtual. You can see which one picks up an email delivery in the logs whenever mail is accepted for delivery (relay=local or relay=virtual) I need to make sure that the local delivery agent does not take over delivery of mail for my virtual domains (which it will do given half a chance even if there is no local user called myemailaccountname). If mydomain is restricted to the local hostname and internet domains are kept out of mydestination (as above), all should be well. I would also suggest clearing out /etc/aliases file (and running newaliases so that the changes take effect) but it is not strictly necessary.

On to virtual reality. I need to tell Postfix which domains to handle as virtual domains and how it should save mail for the virtual users. I should tell it which users to recognise and and which to reject. I need to inform it of any virtual aliases and where and how to save the mail. Let’s take it one thing at a time.


virtual_mailbox_domains is the mydestination for virtual domains. It simply lists the domains that Postfix will attempt virtual delivery for. As I hinted previously, if a domain is listed in both virtual_mailbox_domains and mydomain/mydestination, the local delivery agent takes precedence so properly resetting those local settings as outlisted above is hugely important.

Every domain which I intend to create actual real life email accounts for should be listed here (multiple domains separated by commas). The exception is what we could call ‘shadow domain accounts’, i.e. domains for which all email addresses should be aliases for email addresses on another domain (say @acorp.net for @acorp.com). Those will be dealth with elsewhere.

virtual_mailbox_domains = acorp.com, bcorp.com


So much for the domains, what about the individual accounts? The naming of postfix settings is probably obvious to it’s developers but it really doesn’t help users when you have three almost identical sounding settings and trying to remember which one does what. The good things is that all settings to do with your virtual domains begin with virtual_ so you will not be conflating the local and the virtual settings.

Enter virtual_mailbox_maps. What you are mapping are email accounts to local file paths, i.e. where the email for that particular account should be stored. This setting does not detail the mappings; it just tells postfix where (in a database, a text file, a service, …) it can find them. Like aliases before we simply put these settings into it’s own text file.

virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps

By listing an account in this file I am also telling Postfix that this is a legitimate, real email account on this server and so mail for it should be accepted. If a virtual account is not listed in the virtual_mailbox_maps file, Postfix will reject mail for it out of hand (except for virtual alisases, see later). The format is the same as all Postfix maps files: There is a one-to-one mapping with keys on the left hand side (LHS) and lookups on the right hand side (RHS). In this particular instance you list the email address on the LHS and the relative path on the RHS.

mail@acorp.com             acorp.com/mail/maildir/
webmaster@bcorp.com        bcorp.com/webmaster/maildir/

So mail for mail@acorp.com will go to the subdirectory ‘mail/maildir’ in the folder ‘acorp.com’. Mail for webmaster@bcorp.com will go to the subdirectory ‘webmaster/maildir’ inside the folder ‘bcorp.com’. The paths are relative to the setting virtual_mailbox_base which is an absolute path that varies from distro to distro and system to system (see more below).

The ‘maps’ format is the most basic form of database that Postfix uses. The disadvantage is that it doesn’t scale well: Having to manually maintain thousands of  users in a text file sounds like a job from hell. The advantage is the simplicity: It doesn’t require sysadmin skills to add a user. Whenever I create or edit a maps format file, I follow up by running the command postmap on it. This will create a similarly named file in the same directory only with a .db suffix.

postmap /etc/postfix/virtual_mailbox_maps

It is common in other guides to use MySQL for the same purposes as I use maps here. If you have a working MySQL setup already it may not be such a bad idea. But as a general rule it is shooting sparrows with cannons to use a fullpowered database when a 1k text file will do.

I want to mention two things about the paths listed in the virtual_mailbox_maps file before moving on to other settings.

First, I follow convention in using the domain for folder name and putting the email account username underneath it, thus inverting the email naming system into a folder hierarchy. Nobody says you have to do it this way, it’s just convention. Adding maildir as an additional subdirectory is a concession to Dovecot which prefers to have an account home (‘mail’ and ‘me’ respectively) and keep the actual mail in a folder below that ‘home’. Your MDA may expect something else but if you expect to use Dovecot it’s not a bad idea to follow this example.

Second, an important setting is indicated simply by the way the paths are written. By ending the path in a forward slash (like above) I am telling Postfix to use a directory for storing the email rather than one big file. This is what is known as the maildir format (as opposed to the one-file mbox format). Search for ‘mbox vs maildir’ if you want to get into that discussion. I am going to stick with maildir throughout this series.

virtual_mailbox_base, virtual_uid_maps and virtual_gid_maps

As I said before the mail folders for individual accounts are relative to the absolute path given in virtual_mailbox_base. So let’s set it:

virtual_mailbox_base = /home/vmail

Or /usr/local/vmail or anything really. Keep your email on a separate drive mounted under /mnt or somewhere that makes sense to you. The dominant convention seems merely to be to call the directory vmail (or virtual_mail) regardless of where you stick it. Whatever the virtual_mailbox_base setting, I will need to make parallel settings when I setup the MDA (e.g. Dovecot), as it will not find it by itself.

I also need to establish who owns the mail. The convention to go with the ‘vmail’ folder is to let it be owned by a vmail user and the vmail group. When I set up the local mail, each mbox file was owned by the system user whose mail it was. Since virtual email users do not have a system account I cannot do that here. I check to see if the vmail user hasn’t already been created with the id program (run as root):

[~] id vmail
uid=128(vmail) gid=142(vmail) groups=142(vmail)

From this I get the user id (uid) and the group id (gid) of the vmail user and group. If id reports no such user, I will need to create the user in the usual way and then id the user. I also make sure to chown the virtual_mailbox_base directory so that it is owned by vmail:vmail. Once I have the uid and gid I add them to main.cf:

virtual_uid_maps = static:128
virtual_gid_maps = static:142

Notice that the settings are of the ‘_maps’ type. This is because I could use the settings with a lookup file so that the mail of different virtual users is owned by different system users and groups. If I wanted to do that I would – as always with maps files – indicate a file with hash:/etc/postfix/somefile, create a lookup table with LHS and RHS and run postmap on the file. Instead I keep it even simpler with a pseudo-map which returns the same ‘static’ value regardless of the user. That value is the uid and gid of vmail, respectively. This way all mail for all domains and users is owned by vmail. Again: Keep it simple, stupid.

Once I have checked that all settings are as I want and all maps have been postmapped (check for the .db versions of files and that they are of more recent date than their editable counterparts) I restart Postfix (systemctl restart postfix.service)  so that my changes take effect. I can look through the journal (either using systemctl -l status postfix.service or journalctl -u postfix.service) for errors and warnings.

It’s important to note at this point that this setup builds on the previous post which adressed things like MX and ports. Without that, the configuration here will not accomplish anything.

Testing time: The obvious way to test this setup is by sending email from an outside source, e.g. a webmail or work account, to the new virtual setup. Check the journal and the maildir directory to see if reception is smooth. There is currently no way to access the mail in the ‘normal’ way (i.e. IMAP or POP3) so ls is your friend here.

In the next post I will start on the thorny issue of mail relayal, or using my SMTP server to send email out of the house. I’m also starting to get to the point where I need a working MDA (mail delivery agent) in order to make sense of my MTA (message transfer agent) setup, so maybe I will have to start another series focusing on Dovecot.

Mailboxes © Andrew Taylor, CC BY 2.0

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.