8.2 main.cf

Here is the main.cf file with comments by Wietse Venema and by the author of this document interspersed throughout. The original file is in the white background and our suggested changes and comments are in regular text:

   # Global Postfix configuration file. This file lists only a subset
   # of all 100+ parameters. See the sample-xxx.cf files for a full list.
   # The sample files mentioned above are located in /usr/doc/postfix-19990906_pl06/
   # The general format is lines with parameter = value pairs. Lines
   # that begin with whitespace continue the previous line. A value can
   # contain references to other $names or ${name}s.
   # The queue_directory specifies the location of the Postfix queue.
   # This is also the root directory of Postfix daemons that run chrooted.
   # See the files in examples/chroot-setup for setting up Postfix chroot
   # environments on different UNIX systems.
   queue_directory = /var/spool/postfix

This is the same directory that sendmail uses for the incoming mail queue.

   # The program_directory parameter specifies the default location of
   # Postfix support programs and daemons. This setting can be overruled
   # with the command_directory and daemon_directory parameters.
   program_directory = /some/where/postfix/bin

The line above must be corrected. The RPM installs the Postfix binaries into /usr/libexec/postfix by default.

   # The command_directory parameter specifies the location of all
   # postXXX commands.  The default value is $program_directory.
   command_directory = /usr/sbin

The line above is correct and may be left as is.

   # The daemon_directory parameter specifies the location of all Postfix
   # daemon programs (i.e. programs listed in the master.cf file). The
   # default value is $program_directory. This directory must be owned
   # by root.
   daemon_directory = /usr/libexec/postfix

The line above is correct and may be left as is.

   # The mail_owner parameter specifies the owner of the Postfix queue
   # and of most Postfix daemon processes.  Specify the name of a user
   # don't specify nobody or daemon. PLEASE USE A DEDICATED USER.
   mail_owner = postfix

The line above is correct and may be left as is.

   # The default_privs parameter specifies the default rights used by
   # the local delivery agent for delivery to external file or command.
   # These rights are used in the absence of a recipient user context.
   #default_privs = nobody

The line above is correct and may be left as is but it should be uncommented (e.g. remove the leading pound sign).

   # The myhostname parameter specifies the Internet hostname of this
   # mail system. The default is to use the fully-qualified domain name
   # from gethostname(). $myhostname is used as a default value for many
   # other configuration parameters.
   #myhostname = host.domain.name

Set the value in the line above to the Fully Qualified Domain Name (FQDN) for you machine. E.g. if your hostname is turkey and your domain is trot.com then your FQDN would be “turkey.trot.com”. You will also need to uncomment this line.

   #myhostname = virtual.domain.name

The line above is redundant for most configurations and can usually be left commented.

   # The mydomain parameter specifies the local Internet domain name.
   # The default is to use $myhostname minus the first component.
   # $mydomain is used as a default value for many other configuration
   # parameters.
   #mydomain = domain.name

The line above should be your domain name only without the hostname prepended to the front of it. As in the example we gave above the correct value here would be trot.com. Don't forget to uncomment the line as well.

   # The myorigin parameter specifies the domain that locally-posted
   # mail appears to come from. The default is to append $myhostname,
   # which is fine for small sites.  If you run a domain with multiple
   # machines, you should (1) change this to $mydomain and (2) set up
   # a domain-wide alias database that aliases each user to
   # user@that.users.mailhost.
   #myorigin = $myhostname
   #myorigin = $mydomain

The instructions here are pretty good. Typically what's done here is to let this default to $mydomain. Be sure to uncomment your choice.

   # The inet_interfaces parameter specifies the network interface
   # addresses that this mail system receives mail on.  By default,
   # the software claims all active interfaces on the machine. The
   # parameter also controls delivery of mail to user@[ip.address].
   #inet_interfaces = all

Once again the instructions here are good. Just uncomment the above listed line and you should be fine. Unless you have some odd requirement the next two entries can be left commented. You shouldn't need them.

   #inet_interfaces = $myhostname
   #inet_interfaces = $myhostname, localhost
   # The mydestination parameter specifies the list of domains that this
   # machine considers itself the final destination for.
   # The default is $myhostname + localhost.$mydomain.  On a mail domain
   # gateway, you should also include $mydomain. Do not specify the
   # names of domains that this machine is backup MX host for. Specify
   # those names via the relay_domains or permit_mx_backup settings for
   # the SMTP server (see sample-smtpd.cf.
   # The local machine is always the final destination for mail addressed
   # to user@[the.net.work.address] of an interface that the mail system
   # receives mail on (see the inet_interfaces parameter).
   # Specify a list of host or domain names, /file/name or type:table
   # patterns, separated by commas and/or whitespace. A /file/name
   # pattern is replaced by its contents; a type:table is matched when
   # a name matches a lookup key.  Continue long lines by starting the
   # next line with whitespace.
   #mydestination = $myhostname, localhost.$mydomain
   #mydestination = $myhostname, localhost.$mydomain $mydomain

The most common practice is to select the line immediately above as your choice here. Be sure to uncomment it and put a comma between the last two entries as it appears to have been omitted.

   #mydestination = $myhostname, localhost.$mydomain, $mydomain,
   #       mail.$mydomain, www.$mydomain, ftp.$mydomain
   # The relayhost parameter specifies the default host to send mail to
   # when no entry is matched in the optional transport(5) table. When
   # no relayhost is given, mail is routed directly to the destination.
   # On an intranet, specify the organizational domain name. If your
   # internal DNS uses no MX records, specify the name of the intranet
   # gateway host instead.
   # Specify a domain, host, host:port, [address] or [address:port].
   # Use the form [destination] to turn off MX lookups. See also the
   # default_transport parameter if you're connected via UUCP.
   #relayhost = $mydomain
   #relayhost = gateway.my.domain
   #relayhost = uucphost
   #relayhost = [mail.$mydomain:9999]

If you are behind some sort of a firewall or you need to masquerade the envelope (which will be covered later in this document) you would set the “relayhost” value to the MTA for your domain. If this host is to be *the* MTA for the domain then leave all of these commented out.

   # The default_transport parameter specifies the default message
   # delivery transport to use when no transport is explicitly given in
   # the optional transport(5) table.
   #default_transport = smtp

In most cases the above line would be uncommented and left as is.

   #default_transport = uucp
   # Insert text from sample-rewrite.cf if you need to do address
   # masquerading.
   # Insert text from sample-canonical.cf if you need to do address
   # rewriting, or if you need username->Firstname.Lastname mapping.
   # Insert text from sample-virtual.cf if you need virtual domain support.
   # Insert text from sample-relocated.cf if you need "user has moved"
   # style bounce messages. Alternatively, you can bounce recipients
   # with an SMTP server access table. See sample-smtpd.cf.
   # Insert text from sample-transport.cf if you need explicit routing.
   # The alias_maps parameter specifies the list of alias databases used
   # by the local delivery agent. The default list is system dependent.
   # On systems with NIS, the default is to search the local alias
   # database, then the NIS alias database. See aliases(5) for syntax
   # details.
   # If you change the alias database, run "postalias /etc/aliases" (or
   # wherever your system stores the mail alias file), or simply run
   # "newaliases" to build the necessary DBM or DB file.
   # It will take a minute or so before changes become visible.  Use
   # "postfix reload" to eliminate the delay.
   #alias_maps = dbm:/etc/aliases
   alias_maps = hash:/etc/aliases

The alias_maps line is pointing at the /etc/aliases file which we preserved prior to removing sendmail. Best practice (recommended) usually prefers that all the Postfix config files be kept together so it might be a good idea to change this line to read:

   alias_maps = hash:/etc/postfix/aliases

and also make sure that you put the aliases file in /etc/postfix. Otherwise Postfix will complain on startup and fail to run. The default db type on Red Hat Linux is hash so be sure and use it as we have here. One common error which people have is when they use dbm instead of hash. Don't fall into that trap.

   #alias_maps = hash:/etc/aliases, nis:mail.aliases
   #alias_maps = netinfo:/aliases
   # The alias_database parameter specifies the alias database(s) that
   # are built with "newaliases" or "sendmail -bi".  This is a separate
   # configuration parameter, because alias_maps (see above) may specify
   # tables that are not necessarily all under control by Postfix.
   #alias_database = dbm:/etc/aliases
   #alias_database = dbm:/etc/mail/aliases
   #alias_database = hash:/etc/aliases

As the instructions say if you want to use the newaliases command to handle the aliases file (recommended) you should uncomment the above line but be sure (if you made the path change we recommended in the alias_maps section) and change it to read:

   alias_database = hash:/etc/postfix/aliases

Then be sure to uncomment the line and run the newaliases command before starting Postfix.

   #alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases

If you happen to run majordomo then you should use the line above instead of just the aliases line. Be sure the path to the file majordomo is correct. The best practice convention is to put it into /etc/postfix. Most Red Hat Linux sendmail installations would have had it in /etc/mail/. We will dicuss this a bit more when we get to the listserv section of this document.

   # The prepend_delivered_header controls when Postfix should prepend
   # a Delivered-To: message header.
   # By default, Postfix prepends a Delivered-To: header when forwarding
   # mail and when delivering to file (mailbox) or command.  Turning off
   # the Delivered-To: header when forwarding mail is not recommended.
   # prepend_delivered_header = command, file, forward
   # prepend_delivered_header = forward

The defaults will work fine so you can leave this section commented out unless you have some special need or preference.

   # ADDRESS EXTENSIONS (e.g., user+foo)
   # The recipient_delimiter parameter specifies the separator between
   # user names and address extensions (user+foo). See canonical(5),
   # local(8), relocated(5) and virtual(5) for the effects this has on
   # aliases, canonical, virtual, relocated and .forward file lookups.
   # Basically, the software tries user+foo and .forward+foo before
   # trying user and .forward.
   # recipient_delimiter = +

This one can be left commented out also, unless you have some special need or preference.

   # The home_mailbox parameter specifies the optional pathname of a
   # mailbox relative to a user's home directory. The default is to
   # deliver to the UNIX-style /var/spool/mail/user or /var/mail/user.
   # Specify "Maildir/" for qmail-style delivery (the / is required).
   #home_mailbox = Mailbox
   #home_mailbox = Maildir/

On Red Hat Linux systems you should leave this alone unless you know what you're doing. If you're converting from qmail to Postfix (doubtful) then it would probably be useful.

   # The mail_spool_directory parameter specifies the directory where
   # UNIX-style mailboxes are kept. The default setting depends on the
   # system type.
   # mail_spool_directory = /var/mail
   # mail_spool_directory = /var/spool/mail

The previous line is correct for Red Hat Linux defaults so it should be uncommented and left as is.

   # The mailbox_command parameter specifies the optional external
   # command to use instead of mailbox delivery. The command is run as
   # the recipient with proper HOME, SHELL and LOGNAME environment settings.
   # Exception:  delivery for root is done as $default_user.
   # Other environment variables of interest: USER (recipient username),
   # EXTENSION (address extension), DOMAIN (domain part of address),
   # and LOCAL (the address localpart).
   # Unlike other Postfix configuration parameters, the mailbox_command
   # parameter is not subjected to $parameter substitutions. This is to
   # make it easier to specify shell syntax (see example below).
   # Avoid shell meta characters because they will force Postfix to run
   # an expensive shell process. Procmail alone is expensive enough.
   #mailbox_command = /some/where/procmail

The default MDA on Red Hat Linux systems is procmail. You can use the command “which procmail” to verify the path but unless you've changed procmail's location it is located in “/usr/bin/procmail”. Don't forget to uncomment the line.

   #mailbox_command = /some/where/procmail -a "$EXTENSION"
   # The mailbox_transport specifies the optional transport in master.cf
   # to use after processing aliases and .forward files. This parameter
   # has precedence over the mailbox_command, fallback_transport and
   # luser_relay parameters.
   #mailbox_transport = cyrus

On a default Red Hat Linux system you should leave the above line alone.

   # The fallback_transport specifies the optional transport in master.cf
   # to use for recipients that are not found in the UNIX passwd database.
   # This parameter has precedence over the luser_relay parameter.
   #fallback_transport =

On a default Red Hat Linux system you should leave the above line alone.

   # The luser_relay parameter specifies an optional destination address
   # for unknown recipients.  By default, mail for unknown local recipients
   # is bounced.
   # The following expansions are done on luser_relay: $user (recipient
   # username), $shell (recipient shell), $home (recipient home directory),
   # $recipient (full recipient address), $extension (recipient address
   # extension), $domain (recipient domain), $local (entire recipient
   # localpart), $recipient_delimiter. Specify ${name?value} or
   # ${name:value} to expand value only when $name does (does not) exist.
   # luser_relay = $user@other.host
   # luser_relay = $local@other.host
   # luser_relay = admin+$local

It's your choice what you do here but it can be quite annoying to receive a bazillion bounces a day. Leave this alone (recommended).

   # The controls listed here are only a very small subset. See the file
   # sample-smtpd.cf for an elaborate list of anti-UCE controls.
   # The header_checks parameter restricts what may appear in message
   # headers. This requires that POSIX or PCRE regular expression support
   # is built-in. Specify "/^header-name: stuff you do not want/ REJECT"
   # in the pattern file. Patterns are case-insensitive by default. Note:
   # specify only patterns ending in REJECT. Patterns ending in OK are
   # mostly a waste of cycles.
   #header_checks = regexp:/etc/postfix/filename
   #header_checks = pcre:/etc/postfix/filename

The above section enables a filter which you can use to detect and “bounce” mail which matches a certain regular expression (REGEXP). The difference between using procmail and regexp or PCRE is that these two catch the mail prior to delivery and can effectively block unwanted mail at the SMTP port.

   # The relay_domains parameter restricts what domains (and subdomains
   # thereof) this mail system will relay mail from or to.  See the
   # smtpd_recipient_restrictions restriction in the file sample-smtpd.cf.
   # By default, Postfix relays mail only from or to sites in or below
   # $mydestination, or in the optional virtual domain list.
   # Specify a list of hosts or domains, /file/name patterns or type:name
   # lookup tables, separated by commas and/or whitespace.  Continue
   # long lines by starting the next line with whitespace. A file name
   # is replaced by its contents; a type:name table is matched when a
   # (parent) domain appears as lookup key.
   # NOTE: Postfix will not automatically forward mail for domains that
   # list this system as their primary or backup MX host. See the
   # permit_mx_backup restriction in the file sample-smtpd.cf.
   #relay_domains = $mydestination, $virtual_maps

For anyone who knows already how MX records work this is a critical component in the Postfix configuration. Home users probably won't need this line but anyone who handles mail for mutiple domains will. Here's a sample of how it can be used:

relay_domains = $mydestination, /etc/postfix/relay-domains

In this example the domains you want to relay for would be placed in the file /etc/postfix/relay-domains. One to a line like so:


Note: this file is *not* hashed or mapped. It is a simple text file. You can also use IP addresses instead of names.

   # The mynetworks parameter specifies the list of networks that are
   # local to this machine.  The list is used by the anti-UCE software
   # to distinguish local clients from strangers. See permit_mynetworks
   # and smtpd_recipient_restrictions in the file sample-smtpd.cf file.
   # The default is a list of all networks attached to the machine:  a
   # complete class A network (X.0.0.0/8), a complete class B network
   # (X.X.0.0/16), and so on. If you want stricter control, specify a
   # list of network/mask patterns, where the mask specifies the number
   # of bits in the network part of a host address. You can also specify
   # the absolute pathname of a pattern file instead of listing the
   # patterns here.
   #mynetworks =,

The line above is another critical component in the configuration of Postfix. As the instructions say it specifies the list of networks that are local to this host. For those unfamiliar with the syntax used, it's called Classless Inter-Domain Routing (CIDR) or supernetting. For those familiar with the network classes (A, B, C etc.) it is a way of dividing IP addresses up without reference to class. The way it works is not simple but not overly complicated either.

In the old class based system networks were divided up by class where the network addresses themselves determined what a network's class might be and thus its network address. The old system divided networks up like this: thru were Class “A” networks thru were Class “B” networks thru were Class “C” networks thru were Class “D” networks thru were Class “E” networks

This worked great but it didn't allow blocks of addresses to be sliced up in ways that made sense in an era when IP network addresses are scarce (such as the present day). What was happening was that the Class “B” addresses were running out and large organizations were having to string together groups of Class “C” networks. This was creating an explosion in the size of the Internet's routing tables which was unacceptable so the search began looking for a solution which was both workable and allowed addresses to be partitioned in smaller blocks. The result was CIDR.

CIDR works by identifying explicitly which parts of an address represent the network portion and which parts represent the host portion of a given network address. It has been a boon to an age when address space is getting more and more scarce. Take, for example, this CIDR block:

Let's backtrack for a moment so you can get some of the background information. An IP address is actually made up of a series of bits which are read from left to read like so:


Each sequence of 8 bits is called an octet and each bit in the sequence has a decimal value from left to right like this:

   128, 64, 32, 16, 8, 4, 2, 1 

for each octet. If you add these all together you get 255 but the actual value would be 256 because when we count these we have to start with zero so it's 255+1=256 bits (you really should try not forget that :-).

So the first octet in our CIDR block would be represented like this:

   11000000 == 192

The second would be this:

   10101000 == 168

The third would be this:

   00000001 == 1

and the last would be this:

   00000000 == 0

When we put them all together the bit representation for the entire address would be this: 11000000.10101000.00000001.00000000 which is equivalent to Now when we hang the “/24” on the end of it what does that mean? Well, it's pretty simple... all we're saying is that the first 24 bits in the IP address (from left to right) represent our network and the remaining bits are available to be used as hosts within that network. So if a CIDR block is identified as a “/24” (pronounced “slash twenty four”) we mean that out of 32 available bits in an address the first 24 from left to right represent the network and the remainder (8) may be used as host addresses. So if all 8 bits that remain can be used as network hosts how many would that be? 255? Well... if you add the values of each place-holding bit from left to right you do get 255 but this is incorrect. Always remember as we said above that you must start counting with 0 so the actual range we're dealing with is 0 through 255 for a total of 256 possible values.

Now the way the engineering side of all of this works is that two addresses are lost right off the top. One potential address represents the network (0 in this case) and 255 is reserved as the broadcast address for the network so that actually leaves 254 viable addresses reserved for hosts. So in our example the first avilable host address would be and the last would be

To some, this discussion may appear to be going way beyond what you'd expect to find in a document about MTAs but it's important to understand this because Postfix was written to implement CIDR. Consequently you need to understand how it works and what it means if you want to configure Postfix properly. If this brief explanation hasn't helped you, you may want to spend some time reading on the subject. One excellent primer on TCP/IP is TCP/IP Illustrated, Volume 1 The Protocols, written by W. Richard Stevens and published by Addison Wesley. CIDR is also described in RFC 1518 and 1519.

   #mynetworks = $config_directory/mynetworks
   # The smtpd_banner parameter specifies the text that follows the 220
   # status code in the SMTP greeting banner. Some people like to see
   # the mail version advertised. By default, Postfix shows no version.
   # You MUST specify the $myhostname at the start of the text. When
   # the SMTP client sees its own hostname at the start of an SMTP
   # greeting banner it will report a mailer loop. That's better than
   # having a machine meltdown.
   #smtpd_banner = $myhostname ESMTP $mail_name
   #smtpd_banner = $myhostname ESMTP $mail_name ($mail_version)

The above config entry is a matter of personal preference. It is not required and is up to the administrator to choose.

   # How many parallel deliveries to the same user or domain? With local
   # delivery, it does not make sense to do massively parallel delivery
   # to the same user, because mailbox updates must happen sequentially,
   # and expensive pipelines in .forward files can cause disasters when
   # too many are run at the same time. With SMTP deliveries, 10
   # simultaneous connections to the same domain could be sufficient to
   # raise eyebrows.
   # Each message delivery transport has its XXX_destination_concurrency_limit
   # parameter.  The default is $default_destination_concurrency_limit.
   local_destination_concurrency_limit = 2
   default_destination_concurrency_limit = 10

As the text above says this section is really about rate limiting. It is, essentially, the gas pedal for Postfix. Unless you have some really good reason to change these the defaults should be fine. Once you've run Postfix for a while (particularly those who use it in a professional setting) you may have a better idea of how this should be set for your environment.

   # The debug_peer_level parameter specifies the increment in verbose
   # logging level when an SMTP client or server host name or address
   # matches a pattern in the debug_peer_list parameter.
   debug_peer_level = 2

We recommend the default here unless there is some overriding reason to change this. Debugging will be covered in a later chapter of this document. For what it's worth this section has no real relevance unless the next is enabled.

   # The debug_peer_list parameter specifies an optional list of domain
   # or network patterns, /file/name patterns or type:name tables. When
   # an SMTP client or server host name or address matches a pattern,
   # increase the verbose logging level by the amount specified in the
   # debug_peer_level parameter.
   # debug_peer_list =
   # debug_peer_list = some.domain

This section is used in combination with debug_peer_level so if that's not enabled then this one is moot. This is actually a very neat feature of Postfix. Think about it for a minut. If everything works fine but there is this one host which seems to have problem receiving or sending mail to or from your host then you could use this feature to increase the logging level for just that host.

   # The debugger_command specifies the external command that is executed
   # when a Postfix daemon program is run with the -D option.
   # Use "command .. & sleep 5" so that the debugger can attach before
   # the process marches on. If you use an X-based debugger, be sure to
   # set up your XAUTHORITY environment variable before starting Postfix.
   debugger_command =
            xxgdb $daemon_directory/$process_name $process_id & sleep 5

Leave this section alone for now. We will cover debugging in some detail in a later section of this document.

   # Other configurable parameters.

That's it. We've made it through the main.cf file and we're almost ready to start it up.