Postfix Virtual Aliases Configuration: How to Fix Catch-All Grabbing All Emails Instead of Undefined Addresses


2 views

When configuring Postfix with virtual aliases, many administrators encounter a situation where the catch-all address (@example.com) unexpectedly captures all incoming emails instead of just handling undefined addresses. This happens despite having properly defined specific virtual aliases like info@example.com or sales@example.com.

Here's a typical setup that might cause this issue:

# /etc/postfix/main.cf
virtual_alias_domains = example.com
virtual_alias_maps = hash:/etc/postfix/virtual

# /etc/postfix/virtual
postmaster@example.com account1
info@example.com       account1
sales@example.com      account2
@example.com           catchall

The problem stems from Postfix's matching order for virtual aliases. The catch-all pattern (@domain.com) is actually matching all addresses, including those that should be handled by specific mappings. This occurs because:

  • Postfix processes virtual aliases in the order they appear in the map
  • The catch-all is matching before specific addresses are checked
  • The domain might be listed in both virtual_alias_domains and mydestination

To fix this, we need to:

  1. Ensure the domain is only in virtual_alias_domains
  2. Structure the virtual aliases file correctly
  3. Use proper mapping syntax

Here's the corrected configuration:

# /etc/postfix/main.cf
virtual_alias_domains = example.com, myotherdomain.com
virtual_alias_maps = hash:/etc/postfix/virtual
mydestination = $myhostname, localhost.$mydomain, localhost

# /etc/postfix/virtual
postmaster@example.com    account1
info@example.com          account1
sales@example.com         account2
# Catch-all comes LAST after all specific addresses
@example.com              catchall

The crucial aspect is the ordering in the virtual aliases file. Postfix processes entries from top to bottom, and the first match wins. Therefore:

  • Specific addresses must appear before catch-all
  • More specific patterns should come before less specific ones
  • The catch-all should always be the last entry

After making these changes, test with:

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

For complex setups, consider separating specific aliases and catch-all:

# main.cf
virtual_alias_maps = hash:/etc/postfix/virtual, regexp:/etc/postfix/virtual_catchall

# /etc/postfix/virtual (specific aliases)
postmaster@example.com account1
info@example.com       account1

# /etc/postfix/virtual_catchall (regex catch-all)
/^.*@example\.com$/   catchall
  • Duplicate domain entries in virtual_alias_domains and mydestination
  • Missing postmap command after changing virtual files
  • Incorrect file permissions on virtual aliases files
  • Not restarting/reloading Postfix after changes

When troubleshooting, check:

postconf -n               # Verify active configuration
postmap -q address mapfile # Test specific address mapping
tail -f /var/log/mail.log  # Monitor delivery attempts

When implementing virtual aliases in Postfix 2.10.2 for multi-domain mail routing, a common pitfall occurs where catch-all mappings (@domain.com) unexpectedly override all other defined aliases. This behavior contradicts the expected precedence where specific addresses should take priority over wildcards.

Here's the problematic configuration example:


virtual_alias_domains = example.com
virtual_alias_maps = hash:/etc/postfix/virtual

# /etc/postfix/virtual contents:
postmaster@example.com account1
info@example.com       account1
sales@example.com      account2
@example.com           catchall

The root cause lies in Postfix's mapping evaluation order. While the documentation suggests this pattern, it doesn't explicitly warn about the precedence behavior.

Solution 1: Proper Domain Declaration

First, ensure your domain isn't listed in both virtual_alias_domains and mydestination:


# Correct main.cf settings:
virtual_alias_domains = example.com, myotherdomain.com
mydestination = $myhostname, localhost.$mydomain, localhost

Solution 2: Reordered Virtual Maps

Postfix processes virtual maps sequentially. Place catch-alls after specific mappings:


# /etc/postfix/virtual should be:
postmaster@example.com account1
info@example.com       account1
sales@example.com      account2
# Catch-all comes LAST
@example.com           catchall

Solution 3: Recipient Verification

Add verification to prevent catch-all from grabbing valid addresses:


smtpd_recipient_restrictions =
    ...
    reject_unverified_recipient
    permit_mynetworks
    permit_sasl_authenticated
    reject_unauth_destination

For complex setups, consider using separate map files with proper lookup order:


virtual_alias_maps =
    hash:/etc/postfix/specific_aliases,
    hash:/etc/postfix/catchall

Where specific_aliases contains all defined addresses and catchall contains only the wildcard mapping. This ensures proper evaluation precedence.

Use these commands to verify your configuration:


postmap /etc/postfix/virtual
postfix check
postconf -n
postmap -q "test@example.com" hash:/etc/postfix/virtual

Check mail logs for mapping evaluation:


tail -f /var/log/mail.log | grep virtual