PPoossttffiixx VViirrttuuaall DDoommaaiinn HHoossttiinngg HHoowwttoo

-------------------------------------------------------------------------------

PPuurrppoossee ooff tthhiiss ddooccuummeenntt

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
  * Local files versus network databases
  * As simple as can be: shared domains, UNIX system accounts
  * Postfix virtual ALIAS example: separate domains, UNIX system accounts
  * Postfix virtual MAILBOX example: separate domains, non-UNIX accounts
  * Non-Postfix mailbox store: separate domains, non-UNIX accounts
  * Mail forwarding domains
  * Mailing lists
  * Autoreplies

CCaannoonniiccaall vveerrssuuss hhoosstteedd vveerrssuuss ootthheerr ddoommaaiinnss

Most Postfix systems are ffiinnaall ddeessttiinnaattiioonn 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 ffiinnaall
ddeessttiinnaattiioonn 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 nnoott tthhee ffiinnaall ddeessttiinnaattiioonn 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.

LLooccaall ffiilleess vveerrssuuss nneettwwoorrkk ddaattaabbaasseess

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

    Example: postmap -q info@example.com hash:$config_directory/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 ppoossttmmaapp command to verify that network database lookups produce the exact
same results as local file lookup.

    Example: postmap -q info@example.com ldap:$config_directory/virtual.cf

AAss ssiimmppllee aass ccaann bbee:: sshhaarreedd ddoommaaiinnss,, UUNNIIXX ssyysstteemm aaccccoouunnttss

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.

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

The limitations of this approach are:

  * A total lack of separation: mail for info@my.host.name is delivered to the
    same UNIX system account as mail for info@example.com.
  * With users in the UNIX password file, administration of large numbers of
    users becomes inconvenient.

The examples that follow provide solutions for both limitations.

PPoossttffiixx vviirrttuuaall AALLIIAASS eexxaammppllee:: sseeppaarraattee ddoommaaiinnss,, UUNNIIXX ssyysstteemm aaccccoouunnttss

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 /usr/local/etc/postfix/main.cf:
     2     virtual_alias_domains = example.com ...other hosted domains...
     3     virtual_alias_maps = hash:$config_directory/virtual
     4
     5 /usr/local/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:

  * Line 2: the virtual_alias_domains setting tells Postfix that example.com is
    a so-called virtual alias domain. If you omit this setting then Postfix
    will reject mail (relay access denied) or will not be able to deliver it
    (mail for example.com loops back to myself).

    NEVER list a virtual alias domain name as a mydestination domain!

  * Lines 3-8: the /usr/local/etc/postfix/virtual file contains the virtual aliases. With
    the example above, mail for postmaster@example.com goes to the local
    postmaster, while mail for info@example.com goes to the UNIX account joe,
    and mail for sales@example.com goes to the UNIX account jane. Mail for all
    other addresses in example.com is rejected with the error message "User
    unknown".

  * Line 10: the commented out entry (text after #) shows how one would
    implement a catch-all virtual alias that receives mail for every
    example.com address not listed in the virtual alias file. This is not
    without risk. Spammers nowadays try to send mail from (or mail to) every
    possible name that they can think of. A catch-all mailbox is likely to
    receive many spam messages, and many bounces for spam messages that were
    sent in the name of anything@example.com.

Execute the command "ppoossttmmaapp //eettcc//ppoossttffiixx//vviirrttuuaall" after changing the virtual
file, and execute the command "ppoossttffiixx rreellooaadd" 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.

PPoossttffiixx vviirrttuuaall MMAAIILLBBOOXX eexxaammppllee:: sseeppaarraattee ddoommaaiinnss,, nnoonn--UUNNIIXX aaccccoouunnttss

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 /usr/local/etc/postfix/main.cf:
     2     virtual_mailbox_domains = example.com ...more domains...
     3     virtual_mailbox_base = /var/mail/vhosts
     4     virtual_mailbox_maps = hash:$config_directory/vmailbox
     5     virtual_minimum_uid = 100
     6     virtual_uid_maps = static:5000
     7     virtual_gid_maps = static:5000
     8     virtual_alias_maps = hash:$config_directory/virtual
     9
    10 /usr/local/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 /usr/local/etc/postfix/virtual:
    18     postmaster@example.com postmaster

Notes:

  * Line 2: The virtual_mailbox_domains setting tells Postfix that example.com
    is a so-called virtual mailbox domain. If you omit this setting then
    Postfix will reject mail (relay access denied) or will not be able to
    deliver it (mail for example.com loops back to myself).

    NEVER list a virtual MAILBOX domain name as a mydestination domain!

    NEVER list a virtual MAILBOX domain name as a virtual ALIAS domain!

  * Line 3: The virtual_mailbox_base parameter specifies a prefix for all
    virtual mailbox pathnames. This is a safety mechanism in case someone makes
    a mistake. It prevents mail from being delivered all over the file system.

  * Lines 4, 10-15: The virtual_mailbox_maps parameter specifies the lookup
    table with mailbox (or maildir) pathnames, indexed by the virtual mail
    address. In this example, mail for info@example.com goes to the mailbox at
    /var/mail/vhosts/example.com/info while mail for sales@example.com goes to
    the maildir located at /var/mail/vhosts/example.com/sales/.

  * Line 5: The virtual_minimum_uid specifies a lower bound on the mailbox or
    maildir owner's UID. This is a safety mechanism in case someone makes a
    mistake. It prevents mail from being written to sensitive files.

  * Lines 6, 7: The virtual_uid_maps and virtual_gid_maps parameters specify
    that all the virtual mailboxes are owned by a fixed uid and gid 5000. If
    this is not what you want, specify lookup tables that are searched by the
    recipient's mail address.

  * Line 14: The commented out entry (text after #) shows how one would
    implement a catch-all virtual mailbox address. Be prepared to receive a lot
    of spam, as well as bounced spam that was sent in the name of
    anything@example.com.

    NEVER put a virtual MAILBOX wild-card in the virtual ALIAS file!!

  * Lines 8, 17, 18: As you see, it is possible to mix virtual aliases with
    virtual mailboxes. We use this feature to redirect mail for example.com's
    postmaster address to the local postmaster. You can use the same mechanism
    to redirect an address to a remote address.

  * Line 18: 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.

Execute the command "ppoossttmmaapp //eettcc//ppoossttffiixx//vviirrttuuaall" after changing the virtual
file, execute "ppoossttmmaapp //eettcc//ppoossttffiixx//vvmmaaiillbbooxx" after changing the vmailbox file,
and execute the command "ppoossttffiixx rreellooaadd" 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.

NNoonn--PPoossttffiixx mmaaiillbbooxx ssttoorree:: sseeppaarraattee ddoommaaiinnss,, nnoonn--UUNNIIXX aaccccoouunnttss

This is a variation on the Postfix virtual mailbox example. Again, every hosted
address can have its own mailbox.

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 /usr/local/etc/postfix/main.cf:
     2     virtual_transport = ...see below...
     3     virtual_mailbox_domains = example.com ...more domains...
     4     virtual_mailbox_maps = hash:$config_directory/vmailbox
     5     virtual_alias_maps = hash:$config_directory/virtual
     6
     7 /usr/local/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 /usr/local/etc/postfix/virtual:
    16     postmaster@example.com postmaster

Notes:

  * Line 2: With delivery to a non-Postfix mailbox store for hosted domains,
    the virtual_transport parameter usually specifies the Postfix LMTP client,
    or the name of a master.cf entry that executes non-Postfix software via the
    pipe delivery agent. Typical examples (use only one):

        virtual_transport = lmtp:unix:/path/name (uses UNIX-domain socket)
        virtual_transport = lmtp:hostname:port   (uses TCP socket)
        virtual_transport = maildrop:            (uses pipe(8) to command)

    Postfix comes ready with support for LMTP. And an example maildrop delivery
    method is already defined in the default Postfix master.cf file. See the
    MAILDROP_README document for more details.

  * Line 3: The virtual_mailbox_domains setting tells Postfix that example.com
    is delivered via the virtual_transport that was discussed in the previous
    paragraph. If you omit this virtual_mailbox_domains setting then Postfix
    will either reject mail (relay access denied) or will not be able to
    deliver it (mail for example.com loops back to myself).

    NEVER list a virtual MAILBOX domain name as a mydestination domain!

    NEVER list a virtual MAILBOX domain name as a virtual ALIAS domain!

  * Lines 4, 7-13: The virtual_mailbox_maps parameter specifies the lookup
    table with all valid recipient addresses. The lookup result value is
    ignored by Postfix. In the above example, info@example.com and
    sales@example.com are listed as valid addresses; other mail for example.com
    is rejected with "User unknown" by the Postfix SMTP server. It's left up to
    the non-Postfix delivery agent to reject non-existent recipients from local
    submission or from local alias expansion. If you intend to use LDAP, MySQL
    or PgSQL instead of local files, be sure to review the "local files versus
    databases" section at the top of this document!

  * Line 12: The commented out entry (text after #) shows how one would inform
    Postfix of the existence of a catch-all address. Again, the lookup result
    is ignored by Postfix.

    NEVER put a virtual MAILBOX wild-card in the virtual ALIAS file!!

    Note: if you specify a wildcard in virtual_mailbox_maps, then you still
    need to configure the non-Postfix mailbox store to receive mail for any
    address in that domain.

  * Lines 5, 15, 16: As you see above, it is possible to mix virtual aliases
    with virtual mailboxes. We use this feature to redirect mail for
    example.com's postmaster address to the local postmaster. You can use the
    same mechanism to redirect any addresses to a local or remote address.

  * Line 16: 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.

Execute the command "ppoossttmmaapp //eettcc//ppoossttffiixx//vviirrttuuaall" after changing the virtual
file, execute "ppoossttmmaapp //eettcc//ppoossttffiixx//vvmmaaiillbbooxx" after changing the vmailbox file,
and execute the command "ppoossttffiixx rreellooaadd" after changing the main.cf file.

MMaaiill ffoorrwwaarrddiinngg ddoommaaiinnss

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 /usr/local/etc/postfix/main.cf:
     2     virtual_alias_domains = example.com ...other hosted domains...
     3     virtual_alias_maps = hash:$config_directory/virtual
     4
     5 /usr/local/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:

  * Line 2: The virtual_alias_domains setting tells Postfix that example.com is
    a so-called virtual alias domain. If you omit this setting then Postfix
    will reject mail (relay access denied) or will not be able to deliver it
    (mail for example.com loops back to myself).

    NEVER list a virtual alias domain name as a mydestination domain!

  * Lines 3-11: The /usr/local/etc/postfix/virtual file contains the virtual aliases.
    With the example above, mail for postmaster@example.com goes to the local
    postmaster, while mail for joe@example.com goes to the remote address
    joe@somewhere, and mail for jane@example.com goes to the remote address
    jane@somewhere-else. Mail for all other addresses in example.com is
    rejected with the error message "User unknown".

  * Line 10: The commented out entry (text after #) shows how one would
    implement a catch-all virtual alias that receives mail for every
    example.com address not listed in the virtual alias file. This is not
    without risk. Spammers nowadays try to send mail from (or mail to) every
    possible name that they can think of. A catch-all mailbox is likely to
    receive many spam messages, and many bounces for spam messages that were
    sent in the name of anything@example.com.

Execute the command "ppoossttmmaapp //eettcc//ppoossttffiixx//vviirrttuuaall" after changing the virtual
file, and execute the command "ppoossttffiixx rreellooaadd" 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.

MMaaiilliinngg lliissttss

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:

    /usr/local/etc/postfix/main.cf:
        virtual_alias_maps = hash:$config_directory/virtual

    /usr/local/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.

  * In case of a virtual alias domain, there would need to be one identity
    mapping from each mailing list address to itself.
  * In case of a virtual mailbox domain, there would need to be a dummy mailbox
    for each mailing list address.

AAuuttoorreepplliieess

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:

    /usr/local/etc/postfix/main.cf:
        virtual_alias_maps = hash:$config_directory/virtual

    /usr/local/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!

    /usr/local/etc/postfix/main.cf:
        transport_maps = hash:$config_directory/transport

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

    /usr/local/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.

