Postfix Virtual Domain Hosting Howto


Purpose of this document

This document requires Postfix version 2.0 or later.

This document gives an overview of how Postfix can be used for hosting multiple Internet domains, both for final delivery on the machine itself and for the purpose of forwarding to destinations elsewhere.

The text not only describes delivery mechanisms that are built into Postfix, but also gives pointers for using non-Postfix mail delivery software.

The following topics are covered:

Canonical versus hosted versus other domains

Most Postfix systems are final destination for only a few domain names. These include the hostnames and [the IP addresses] of the machine that Postfix runs on, and sometimes also include the parent domain of the hostname. The remainder of this document will refer to these domains as the canonical domains. They are usually implemented with the Postfix local domain address class, as defined in the ADDRESS_CLASS_README file.

Besides the canonical domains, Postfix can be configured to be final destination for any number of additional domains. These domains are called hosted, because they are not directly associated with the name of the machine itself. Hosted domains are usually implemented with the virtual alias domain address class and/or with the virtual mailbox domain address class, as defined in the ADDRESS_CLASS_README file.

But wait! There is more. Postfix can be configured as a backup MX host for other domains. In this case Postfix is not the final destination for those domains. It merely queues the mail when the primary MX host is down, and forwards the mail when the primary MX host becomes available. This function is implemented with the relay domain address class, as defined in the ADDRESS_CLASS_README file.

Finally, Postfix can be configured as a transit host for sending mail across the internet. Obviously, Postfix is not final destination for such mail. This function is available only for authorized clients and/or users, and is implemented by the default domain address class, as defined in the ADDRESS_CLASS_README file.

Local files versus network databases

The examples in this text use table lookups from local files such as DBM or Berkeley DB. These are easy to debug with the postmap command:

Example: postmap -q info@example.com hash:/etc/postfix/virtual

See the documentation in LDAP_README, MYSQL_README and PGSQL_README for how to replace local files by databases. The reader is strongly advised to make the system work with local files before migrating to network databases, and to use the postmap command to verify that network database lookups produce the exact same results as local file lookup.

Example: postmap -q info@example.com ldap:/etc/postfix/virtual.cf

As simple as can be: shared domains, UNIX system accounts

The simplest method to host an additional domain is to add the domain name to the domains listed in the Postfix mydestination configuration parameter, and to add the user names to the UNIX password file.

This approach makes no distinction between canonical and hosted domains. Each username can receive mail in every domain.

In the examples we will use "example.com" as the domain that is being hosted on the local Postfix machine.

/etc/postfix/main.cf:
    mydestination = $myhostname localhost.$mydomain ... example.com

The limitations of this approach are:

The examples that follow provide solutions for both limitations.

Postfix virtual ALIAS example: separate domains, UNIX system accounts

With the approach described in this section, every hosted domain can have its own info etc. email address. However, it still uses UNIX system accounts for local mailbox deliveries.

With virtual alias domains, each hosted address is aliased to a local UNIX system account or to a remote address. The example below shows how to use this mechanism for the example.com domain.

 1 /etc/postfix/main.cf:
 2     virtual_alias_domains = example.com ...other hosted domains...
 3     virtual_alias_maps = hash:/etc/postfix/virtual
 4 
 5 /etc/postfix/virtual:
 6     postmaster@example.com postmaster
 7     info@example.com       joe
 8     sales@example.com      jane
 9     # Uncomment entry below to implement a catch-all address
10     # @example.com         jim
11     ...virtual aliases for more domains...

Notes:

Execute the command "postmap /etc/postfix/virtual" after changing the virtual file, and execute the command "postfix reload" after changing the main.cf file.

Note: virtual aliases can resolve to a local address or to a remote address, or both. They don't have to resolve to UNIX system accounts on your machine.

More details about the virtual alias file are given in the virtual(5) manual page, including multiple addresses on the right-hand side.

Virtual aliasing solves one problem: it allows each domain to have its own info mail address. But there still is one drawback: each virtual address is aliased to a UNIX system account. As you add more virtual addresses you also add more UNIX system accounts. The next section eliminates this problem.

Postfix virtual MAILBOX example: separate domains, non-UNIX accounts

As a system hosts more and more domains and users, it becomes less desirable to give every user their own UNIX system account.

With the Postfix virtual(8) mailbox delivery agent, every recipient address can have its own virtual mailbox. Unlike virtual alias domains, virtual mailbox domains do not need the clumsy translation from each recipient addresses into a different address, and owners of a virtual mailbox address do not need to have a UNIX system account.

The Postfix virtual(8) mailbox delivery agent looks up the user mailbox pathname, uid and gid via separate tables that are searched with the recipient's mail address. Maildir style delivery is turned on by terminating the mailbox pathname with "/".

If you find the idea of multiple tables bothersome, remember that you can migrate the information (once it works), to an SQL database. If you take that route, be sure to review the "local files versus databases" section at the top of this document.

Here is an example of a virtual mailbox domain "example.com":

 1 /etc/postfix/main.cf:
 2     virtual_mailbox_domains = example.com ...more domains...
 3     virtual_mailbox_base = /var/mail/vhosts
 4     virtual_mailbox_maps = hash:/etc/postfix/vmailbox
 5     virtual_minimum_uid = 100
 6     virtual_uid_maps = static:5000
 7     virtual_gid_maps = static:5000
 8     virtual_alias_maps = hash:/etc/postfix/virtual
 9 
10 /etc/postfix/vmailbox:
11     info@example.com    example.com/info
12     sales@example.com   example.com/sales/
13     # Comment out the entry below to implement a catch-all.
14     # @example.com      example.com/catchall
15     ...virtual mailboxes for more domains...
16 
17 /etc/postfix/virtual:
18     postmaster@example.com postmaster

Notes:

Execute the command "postmap /etc/postfix/virtual" after changing the virtual file, execute "postmap /etc/postfix/vmailbox" after changing the vmailbox file, and execute the command "postfix reload" after changing the main.cf file.

Note: mail delivery happens with the recipient's UID/GID privileges specified with virtual_uid_maps and virtual_gid_maps. Postfix 2.0 and earlier will not create mailDIRs in world-writable parent directories; you must create them in advance before you can use them. Postfix may be able to create mailBOX files by itself, depending on parent directory write permissions, but it is safer to create mailBOX files ahead of time.

More details about the virtual mailbox delivery agent are given in the virtual(8) manual page.

Non-Postfix mailbox store: separate domains, non-UNIX accounts

This is a variation on the Postfix virtual mailbox example. Again, every hosted address can have its own mailbox. However, most parameters that control the virtual(8) delivery agent are no longer applicable: only virtual_mailbox_domains and virtual_mailbox_maps stay in effect. These parameters are needed to reject mail for unknown recipients.

While non-Postfix software is being used for final delivery, some Postfix concepts are still needed in order to glue everything together. For additional background on this glue you may want to take a look at the virtual mailbox domain class as defined in the ADDRESS_CLASS_README file.

The text in this section describes what things should look like from Postfix's point of view. See CYRUS_README or MAILDROP_README for specific information about Cyrus or about Courier maildrop.

Here is an example for a hosted domain example.com that delivers to a non-Postfix delivery agent:

 1 /etc/postfix/main.cf:
 2     virtual_transport = ...see below...
 3     virtual_mailbox_domains = example.com ...more domains...
 4     virtual_mailbox_maps = hash:/etc/postfix/vmailbox
 5     virtual_alias_maps = hash:/etc/postfix/virtual
 6 
 7 /etc/postfix/vmailbox:
 8     info@example.com    whatever
 9     sales@example.com   whatever
10     # Comment out the entry below to implement a catch-all.
11     # Configure the mailbox store to accept all addresses.
12     # @example.com      whatever
13     ...virtual mailboxes for more domains...
14 
15 /etc/postfix/virtual:
16     postmaster@example.com postmaster

Notes:

Execute the command "postmap /etc/postfix/virtual" after changing the virtual file, execute "postmap /etc/postfix/vmailbox" after changing the vmailbox file, and execute the command "postfix reload" after changing the main.cf file.

Mail forwarding domains

Some providers host domains that have no (or only a few) local mailboxes. The main purpose of these domains is to forward mail elsewhere. The following example shows how to set up example.com as a mail forwarding domain:

 1 /etc/postfix/main.cf:
 2     virtual_alias_domains = example.com ...other hosted domains...
 3     virtual_alias_maps = hash:/etc/postfix/virtual
 4 
 5 /etc/postfix/virtual:
 6     postmaster@example.com postmaster
 7     joe@example.com        joe@somewhere
 8     jane@example.com       jane@somewhere-else
 9     # Uncomment entry below to implement a catch-all address
10     # @example.com         jim@yet-another-site
11     ...virtual aliases for more domains...

Notes:

Execute the command "postmap /etc/postfix/virtual" after changing the virtual file, and execute the command "postfix reload" after changing the main.cf file.

More details about the virtual alias file are given in the virtual(5) manual page, including multiple addresses on the right-hand side.

Mailing lists

The examples that were given above already show how to direct mail for virtual postmaster addresses to a local postmaster. You can use the same method to direct mail for any address to a local or remote address.

There is one major limitation: virtual aliases and virtual mailboxes can't directly deliver to mailing list managers such as majordomo. The solution is to set up virtual aliases that direct virtual addresses to the local delivery agent:

/etc/postfix/main.cf:
    virtual_alias_maps = hash:/etc/postfix/virtual

/etc/postfix/virtual:
    listname-request@example.com listname-request
    listname@example.com         listname
    owner-listname@example.com   owner-listname

/etc/aliases:
    listname: "|/some/where/majordomo/wrapper ..."
    owner-listname: ...
    listname-request: ...

This example assumes that in main.cf, $myorigin is listed under the mydestination parameter setting. If that is not the case, specify an explicit domain name on the right-hand side of the virtual alias table entries or else mail will go to the wrong domain.

More information about the Postfix local delivery agent can be found in the local(8) manual page.

Why does this example use a clumsy virtual alias instead of a more elegant transport mapping? The reason is that mail for the virtual mailing list would be rejected with "User unknown". In order to make the transport mapping work one would still need a bunch of virtual alias or virtual mailbox table entries.

Autoreplies

In order to set up an autoreply for virtual recipients while still delivering mail as normal, set up a rule in a virtual alias table:

/etc/postfix/main.cf:
    virtual_alias_maps = hash:/etc/postfix/virtual

/etc/postfix/virtual:
    user@domain.tld user@domain.tld, user@domain.tld@autoreply.mydomain.tld

This delivers mail to the recipient, and sends a copy of the mail to the address that produces automatic replies. The address can be serviced on a different machine, or it can be serviced locally by setting up a transport map entry that pipes all mail for autoreply.mydomain.tld into some script that sends an automatic reply back to the sender.

DO NOT list autoreply.mydomain.tld in mydestination!

/etc/postfix/main.cf:
    transport_maps = hash:/etc/postfix/transport

/etc/postfix/transport:
    autoreply.mydomain.tld  autoreply:

/etc/postfix/master.cf:
    # =============================================================
    # service type  private unpriv  chroot  wakeup  maxproc command
    #               (yes)   (yes)   (yes)   (never) (100)
    # =============================================================
    autoreply unix  -       n       n       -       -       pipe
        flags= user=nobody argv=/path/to/autoreply $sender $mailbox

This invokes /path/to/autoreply with the sender address and the user@domain.tld recipient address on the command line.

For more information, see the pipe(8) manual page, and the comments in the Postfix master.cf file.