Virtual domains

There is a newer issue of thie ISPmail guide available if you are using Debian Jessie!

Let us start with some basic theory on email domains that Postfix can handle. Postfix distinguishes between three kinds of domains. This is a very important concept that you need to understand. Probably half of the support request of desperate readers is caused by misunderstandings here. So please do not skip this section but take your time understanding the matter.

This page is just for explaining the concept – there is nothing to be done on your server yet. For even more confusing information see also the documentation on virtual domains on the Postfix website. ūüôā

Local domains

Postfix is the software component that speaks SMTP and sends and receives emails from the internet. Typically Postfix knows about¬†local domains¬†and¬†local users. A local user is just a normal system user – one that is listed in the /etc/passwd file. This means that¬†all¬†system users will get emails for¬†any¬†local domain. The “mydestination” configuration setting lists all local domains. Example:

mydestination =,,

Let’s say you created a system user “johndoe” (e.g. using the “adduser” command). This simple setup will make Postfix receive emails for


Postfix automatically receives emails for these users and saves them under /var/mail/johndoe. The limitation is that you can’t make johndoe’s account just work in one of these domain. So this is not feasible for different users in different domains. Neither will it work well with many users as you had to create system accounts for each of them. Is it still a good idea to set up at least one local domain in case of configuration or operation problems with other types of domains. If you don’t feel creative and do not really need a local domain then “mydestination = localhost” is a safe choice.

Virtual mailbox domains

This type of domains is the most important type in this tutorial. A virtual mailbox domain is also a domain used to receive email. But you do not use system users (/etc/passwd) to specify valid email addresses in that domain. Instead you explicitly tell Postfix which addresses are valid. A simple way to configure such domains and users is using text files. Consider the following mapping of recipient email addresses to mailboxes on the disk:

Virtual user Virtual mailbox location on disk /var/vmail/ /var/vmail/ /var/vmail/

You have two domains: and So first you will have to tell Postfix about these domains. This is done by setting

virtual_mailbox_domains =

in your Postfix configuration. Next you need to tell Postfix which email addresses you are ready to receive email for and where to store the received emails on disk. The respective text file could be stored in /etc/postfix/virtual_mailbox_users and would look like this:  /var/vmail/  /var/vmail/  /var/vmail/

As you can see the valid email addresses are specified in the left column. And the place on disk where the emails for each recipient address are stored is specified in the right column.¬†In Postfix documentation you may also find the acronym LHS for “left hand side” – this means the left column. Equally the RHS is the “right hand side” – the right column. Such a table with two columns is also called a mapping¬†or¬†hash table.

In the above example I have just hardcoded the virtual domains in the Postfix configuration file (“virtual_mailbox_domains =”). Obviously with many domains this is not feasable. That is why you can also use a mapping file¬†to configure the domains. Let’s assume you created such a file /etc/postfix/virtual_mailbox_domains and it looks like this:  OK  OK

You may wonder why we can’ t just list the domains one-per-line in this file. The reason is that a mapping file always has two columns. In such a “one-dimensional” mapping (list of domains) Postfix does not care about your second column. It does not even have to be “OK” – it can be anything.

If you decided that such text files are okay for you then you will still have to compile these files by running the “postmap” command on them. Example:

postmap /etc/postfix/virtual_mailbox_domains
postmap /etc/postfix/virtual_mailbox_users

postmap will create create additional files based on the above file names but with a “.db” suffix. Postfix will not do that automatically – this is a common caveat. And Postfix will only obey the *.db files. So do not forget to run postmap after you changed a mapping file. To make these mappings known to Postfix you would add these lines to your configuration file:

virtual_mailbox_domains = hash:/etc/postfix/virtual_mailbox_domains
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_users

Now you have the tools you need to set up thousands of domains and email accounts in two text files. That’s nice. But actually I promised that we will store such data in a MySQL database. Fortunately this is not much harder than using the text files. Remember: a mapping is simply a way to assign one value to another . So all you have to do is tell Postfix how to access the two columns of a mapping from a database table.¬†This is done using ‘.cf’ configuration files (see also¬†¬†or run “man 5 mysql_table” in the shell).

Example file:

# Information on how to connect to your MySQL server
user = someone
password = some_password
hosts =

# The database name on the MySQL server
dbname = mailserver

# The SQL query string
query = SELECT mailbox_path FROM virtual_users WHERE email_address=’%s’

Imagine that you have a database table for virtual users with two columns. The left-hand side is the “email_address” column in that table. And the right-hand side is the “mailbox_path” column in that table. So this SQL query gets the right-hand side (mailbox path) for a given email address (email_address). The “%s” is the placeholder for the left-hand side and is filled by Postfix on every lookup.

Note that a lookup here must only return just one row from the database. Postfix must uniquely know where the mailbox path for a given user is. There are other mappings though where it’s allowed to have multiple right-hand side items for one left-hand side item – for example in virtual aliases as shown in the next section.

To use the above configuration file you have to configure it in Postfix’s file:

virtual_mailbox_maps =

If later you find that this mapping is not doing what you intended then the “postmap -q” command can be used to ask Postfix what the right-hand side value for a given left-side value would be. Say that you are interested in the mailbox_path for the email_address “”:

postmap -q

Postfix will then run the above SQL query with your “” argument:

SELECT mailbox_path FROM virtual_users WHERE email_address=’’

The result should be:


(Note: In this tutorial we will not let Postfix deliver the email directly. Rather it hands over incoming email to Dovecot. So we won’t use the above virtual_mailbox_maps in this tutorial. It is still important to understand how Postfix deals with virtual users.)

Virtual alias domains

Virtual alias domains¬†are used for forwarding email from an email address to one or more other email addresses.¬†Virtual alias domains can’t receive email though and save them to disk on your server. They only forward mail somewhere else. The¬†virtual_alias_maps¬†mapping contains forwardings (source, destination) of users or domains to other email addresses or entire domains. Incidentally¬†virtual_alias_maps¬†are obeyed for any received email. So in most cases you do not really need virtual alias domains as you can declare all domains as¬†virtual mailbox domains¬†and use¬†virtual alias maps¬†for forwarding purposes. Technically defining a domain as a¬†virtual alias domain¬†makes Postfix accept email for that domain but you still need an entry in the¬†virtual_alias_maps¬†mapping to tell Postfix where to forward the email.

A note on the virtual_alias_maps: they can return multiple right-hand side destinations (to forward to) for one left-hand side source (the original recipient). You can use that to forward copies of an email to several recipients.

Example 1: forward all email for to

This one is simple example. You have the source ( and the destination ( of the forwarding. John will never get the email.

Example 2: forward all email for to but also receive a copy

This is a bit trickier. If Postfix queries this mapping for it will get two results. (Postfix is smart enough not to create a loop but to understand that you want to get a copy of the emaill.) This is the same as one row with the recipients seperated by commas:,

Example 3: forward all email for any user in the domain to

This is called a catch-all alias. It will accept email for any user in the domain and forward it to If would not be an explicitly defined virtual user then her email would be caught by the catch-all alias and forwarded to

Beware: Catch-all aliases catch spam. Lots of spam. They may look comfortable because they forward all email to one person without the need for creating aliases. But spammers often try to guess email addresses at a known domain. And with a catch-all alias you will receive spam for any of those guessed email addresses. Avoid them and rather define the existing email addresses. Even if it is more work.

13 thoughts on “Virtual domains

    • 2013-11-11 at 09:52

      Hi Andy… oh, right, must have been a copy-paste error from my old notes. Thank you. Corrected.

  • 2014-04-12 at 22:42

    In the table for virtual users, you show the email locations at /var/mail//, but then in the file configuration you show it as /var/vmail//. Is this a typo or really how it is?

    • 2014-04-13 at 00:04

      Thanks for pointing that out, Russ. I just fixed the typo. Must have been an example I copied from the wrong place.

  • 2014-06-04 at 08:27

    I’m missing an Example 4 in the Virtual Domain Alias section for mapping any virtual user on one domain to the alias domain. This is for the case when I have several domains, but only one is my main domain (e.g., and all the others are just aliases (e.g.,,, which forward any incoming traffic to the main domain:

    Technically this works (albeit with a different syntax, I guess) according to my provider, but the problem is as long as there is no explicit 1:1 mapping of users from one domain to the other automatic recipient validation of postfix won’t work. In other words, when you send an email to a non-existing user to the alias domain (e.g. there will be no automatic reply saying that there is no corresponding mailbox, but the email will be discarded silently.

    Noel Jones on the postfix-users mailing list has explained it this way.

    Is this really the end of the story for generic domain aliasing? Does one have to (manually) create aliases for each and every existing user of the final domain? Is there no generic configuration option that preserves recipient validation? Could you explain this in a final example?

  • 2014-06-23 at 21:38


    When installing Debian Wheezy, don’t I have to put in the domain name that
    I have registered. ( -as an example-. I can’t tell, according
    to the tutorial, if “local domains” are necessary or
    not. Or is it just a setting for Postfix? Doesn’t Apache2 require a
    domain name?(ie. a fully qualified domain name) (host/domain)

    Paul G.

    • 2014-06-27 at 05:17

      Apache will try to use your hostname ie debian.local.domain or you can tell it what to use with the ServerName directive. I recomend adding this local domain in your database so you can use it to get any mail from daemons such as cron.

      alias_maps = $virtual_alias_maps

  • 2015-01-12 at 10:48

    Thank you ūüôā

  • 2016-05-02 at 14:49

    Maybe it is neccessary to better highlight that you need to set ‘mydestination = localhost’ in postfix If you leave your virtual domains in that line, postfix ‘ll try to deliver it to local users instead of the virtual users. ‘mydestination’ is filled in automatically at installing postfix, and some user might have trouble tracing delivery problems due to local domain search instead of virtual. ūüôā


Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.